This guide will help you implement Linkrunner functionality in your native iOS application.

Initialization

Initialize the Linkrunner SDK in your app’s startup code, typically in your AppDelegate or SceneDelegate: You can find your project token here. Note: The initialization method doesn’t return any value. To get attribution data and deeplink information, use the getAttributionData method.
import Linkrunner
import SwiftUI

@main
struct MyApp: App {
    init() {
        Task {
            do {
                try await LinkrunnerSDK.shared.initialize(
                    token: "YOUR_PROJECT_TOKEN",
                    keyId: "YOUR_KEY_ID", // Required for SDK signing
                    secretKey: "YOUR_SECRET_KEY" // Required for SDK signing
                )
                print("Linkrunner initialized successfully")
            } catch {
                print("Error initializing Linkrunner:", error)
            }
        }
    }

    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}

SDK Signing Parameters (Optional)

For enhanced security, the LinkRunner SDK requires the following signing parameters during initialization:
  • secretKey: A unique secret key used for request signing and authentication
  • keyId: A unique identifier for the key pair used in the signing process
You can find your project token, secret key, and key ID here.

User Registration

Call the signup method once after the user has completed your app’s onboarding process: It is strongly recommended to use the integrated platform’s identify function to set a persistent user_id once it becomes available (typically after signup or login). If the platform’s identifier function is not called, you must provide a user identifier for Mixpanel, PostHog, and Amplitude integration.
  • mixpanelDistinctId for Mixpanel
  • amplitudeDeviceId for Amplitude
  • posthogDistinctId for PostHog
func onSignup() async {
    do {
        let userData = UserData(
            id: "123", // Required: User ID
            name: "John Doe", // Optional
            phone: "9876543210", // Optional
            email: "user@example.com", // Optional
            isFirstTimeUser: isFirstTimeUser,
            userCreatedAt: "2022-01-01T00:00:00Z", // Optional
            mixPanelDistinctId: "mixpanelDistinctId", // Optional - Mixpanel Distinct ID
            amplitudeDeviceId: "amplitudeDeviceId", // Optional - Amplitude Device ID
            posthogDistinctId: "posthogDistinctId" // Optional - PostHog Distinct ID
        )

        try await LinkrunnerSDK.shared.signup(
            userData: userData,
            additionalData: [:] // Optional: Any additional data
        )

        print("Signup successful")
    } catch {
        print("Error during signup:", error)
    }
}

Getting Attribution Data

To get attribution data and deeplink information for the current installation, use the getAttributionData function:
func getAttributionInfo() async {
    do {
        let attributionData = try await LinkrunnerSDK.shared.getAttributionData()
        print("Attribution data:", attributionData)

        // Attribution data includes:
        // - deeplink: The deep link URL that led to app installation
        // - campaignData: Campaign information
        if let deeplink = attributionData.deeplink {
            print("Deeplink:", deeplink)
            // Handle the deeplink in your app
        }
    } catch {
        print("Error getting attribution data:", error)
    }
}
struct AttributionData: Codable, Sendable {
    let deeplink: String?
    let campaignData: CampaignData
    let attributionSource: String
}

struct CampaignData: Codable, Sendable {
    let id: String
    let name: String
    let adNetwork: String?
    let type: String
    let installedAt: String
    let storeClickAt: String?
    let groupName: String
    let assetName: String
    let assetGroupName: String
}

Setting User Data

Call setUserData each time the app opens and the user is logged in:
func setUserData() async {
    do {
        let userData = UserData(
            id: "123", // Required: User ID
            name: "John Doe", // Optional
            phone: "9876543210", // Optional
            email: "user@example.com" // Optional
        )

        try await LinkrunnerSDK.shared.setUserData(userData)
        print("User data set successfully")
    } catch {
        print("Error setting user data:", error)
    }
}

Setting CleverTap ID

Use setAdditionalData to add CleverTap ID to the SDK:
func setAdditionalData() async {
    do {
        let integrationData = IntegrationData(
            clevertapId:clevertapId
        )

        try await LinkrunnerSDK.shared.setAdditionalData(integrationData)
        print("CleverTap ID set successfully")
    } catch {
        print("Error setting cleverTap ID:", error)
    }
}

Tracking Custom Events

Track custom events in your app:
func trackEvent() async {
    do {
        try await LinkrunnerSDK.shared.trackEvent(
            eventName: "purchase_initiated", // Event name
            eventData: [ // Optional: Event data
                "product_id": "12345",
                "category": "electronics",
                "amount": 99.99 // Include amount as a number for revenue sharing with ad networks like Google and Meta
            ]
        )
        print("Event tracked successfully")
    } catch {
        print("Error tracking event:", error)
    }
}

Revenue Sharing with Ad Networks

To enable revenue sharing with ad networks like Google Ads and Meta, include an amount parameter as a number in your custom event data. This allows the ad networks to optimize campaigns based on the revenue value of conversions:
func trackPurchaseEvent() async {
    do {
        try await LinkrunnerSDK.shared.trackEvent(
            eventName: "purchase_completed",
            eventData: [
                "product_id": "12345",
                "category": "electronics",
                "amount": 149.99 // Revenue amount as a number
            ]
        )
        print("Purchase event with revenue tracked successfully")
    } catch {
        print("Error tracking purchase event:", error)
    }
}
For revenue sharing with ad networks to work properly, ensure the amount parameter is passed as a number (Double or Int), not as a string.

Revenue Tracking

Capturing Payments

Track payment information:
func capturePayment() async {
    do {
        try await LinkrunnerSDK.shared.capturePayment(
            amount: 99.99, // Payment amount
            userId: "user123", // User identifier
            paymentId: "payment456", // Optional: Unique payment identifier
            type: .firstPayment, // optional
            status: .completed // optional
        )
        print("Payment captured successfully")
    } catch {
        print("Error capturing payment:", error)
    }
}

Available Payment Types

public enum PaymentType: String, Sendable {
    case firstPayment = "FIRST_PAYMENT"
    case walletTopup = "WALLET_TOPUP"
    case fundsWithdrawal = "FUNDS_WITHDRAWAL"
    case subscriptionCreated = "SUBSCRIPTION_CREATED"
    case subscriptionRenewed = "SUBSCRIPTION_RENEWED"
    case oneTime = "ONE_TIME"
    case recurring = "RECURRING"
    case `default` = "DEFAULT"
}

Available Payment Statuses

public enum PaymentStatus: String, Sendable {
    case initiated = "PAYMENT_INITIATED"
    case completed = "PAYMENT_COMPLETED"
    case failed = "PAYMENT_FAILED"
    case cancelled = "PAYMENT_CANCELLED"
}

Removing Payments

Remove payment records (for refunds or cancellations):
func removePayment() async {
    do {
        try await LinkrunnerSDK.shared.removePayment(
            userId: "user123", // User identifier
            paymentId: "payment456" // Optional: Unique payment identifier
        )
        print("Payment removed successfully")
    } catch {
        print("Error removing payment:", error)
    }
}

Enhanced Privacy Controls

The SDK offers options to enhance user privacy:
// Enable PII (Personally Identifiable Information) hashing
LinkrunnerSDK.shared.enablePIIHashing(true)

// Check if PII hashing is enabled
let isHashingEnabled = LinkrunnerSDK.shared.isPIIHashingEnabled()
When PII hashing is enabled, sensitive user data like name, email, and phone number are hashed using SHA-256 before being sent to Linkrunner servers.

Function Placement Guide

FunctionWhere to PlaceWhen to Call
LinkrunnerSDK.shared.initializeApp initializationOnce when app starts
LinkrunnerSDK.shared.getAttributionDataAttribution data handling flowWhenever the attribution data is needed
LinkRunner.getInstance().setAdditionalDataIntegration codeWhen third-party integration IDs are available
LinkrunnerSDK.shared.signupOnboarding flowOnce after user completes onboarding
LinkrunnerSDK.shared.setUserDataAuthentication logicEvery time app opens with logged-in user
LinkrunnerSDK.shared.triggerDeeplinkAfter navigation initOnce after your navigation is ready to handle deep links
LinkrunnerSDK.shared.trackEventThroughout appWhen specific user actions occur
LinkrunnerSDK.shared.capturePaymentPayment processingWhen user makes a payment
LinkrunnerSDK.shared.removePaymentRefund flowWhen payment needs to be removed

Complete Example

Here’s a simplified example showing how to integrate Linkrunner in a SwiftUI iOS app: You can find your project token here.
import SwiftUI
import Linkrunner

@main
struct MyApp: App {
    init() {
        Task {
            await initializeLinkrunner()
        }
    }

    func initializeLinkrunner() async {
        do {
            try await LinkrunnerSDK.shared.initialize(token: "YOUR_PROJECT_TOKEN")
            print("Linkrunner initialized successfully")
        } catch {
            print("Error initializing Linkrunner:", error)
        }
    }

    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}

struct ContentView: View {
    var body: some View {
        NavigationView {
            VStack(spacing: 20) {
                Text("Linkrunner Demo")
                    .font(.largeTitle)

                Button("Track Event") {
                    Task {
                        await trackCustomEvent()
                    }
                }
                .buttonStyle(.borderedProminent)
            }
            .padding()
            .onAppear {
                Task {
                    await triggerDeeplink()
                }
            }
        }
    }

    func triggerDeeplink() async {
        await LinkrunnerSDK.shared.triggerDeeplink()
    }

    func trackCustomEvent() async {
        do {
            try await LinkrunnerSDK.shared.trackEvent(
                eventName: "button_clicked",
                eventData: ["screen": "home"]
            )
            print("Event tracked successfully")
        } catch {
            print("Error tracking event:", error)
        }
    }
}

Support

If you encounter issues during integration, contact us at darshil@linkrunner.io.