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.

import Linkrunner
import SwiftUI

@main
struct MyApp: App {
    init() {
        Task {
            do {
                let initResponse = 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:", initResponse)
            } 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.

Response from initialize

The initialization method returns useful data including:

struct LRInitResponse: Codable, Sendable {
    let attributionSource: String
    let campaignData: CampaignData?
    let deeplink: String?
    let ipLocationData: LRIPLocationData
    let rootDomain: Bool
}

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
        )

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

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

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)
    }
}

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"
            ]
        )
        print("Event tracked successfully")
    } catch {
        print("Error tracking event:", error)
    }
}

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.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 {
            let initResponse = try await LinkrunnerSDK.shared.initialize(token: "YOUR_PROJECT_TOKEN")
            print("Linkrunner initialized successfully:", initResponse)
        } 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.