If you plan to use IDFA (Identifier for Advertisers), you need to request permission from the user through App Tracking Transparency. Add the following to your Info.plist file:
<key>NSUserTrackingUsageDescription</key><string>This identifier will be used to deliver personalized ads and improve your app experience.</string>
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 Linkrunnerimport SwiftUI@mainstruct MyApp: App { init() { Task { do { try await LinkrunnerSDK.shared.initialize( token: "YOUR_PROJECT_TOKEN", secretKey: "YOUR_SECRET_KEY", // Optional: Required for SDK signing keyId: "YOUR_KEY_ID", // Optional: Required for SDK signing debug: true // Optional: Enable debug mode for development (defaults to false) ) print("Linkrunner initialized successfully") } catch { print("Error initializing Linkrunner:", error) } } } var body: some Scene { WindowGroup { ContentView() } }}
Call the signup method as soon as the user is identified — whether through signup or login. This is the moment Linkrunner ties the install (and any future events) to a user identifier.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).
To enable remarketing and reattribution, you need to capture deep links and pass them to the Linkrunner SDK. This allows Linkrunner to detect returning users who open the app via a deep link.Add the following to your SceneDelegate.swift:
class SceneDelegate: UIResponder, UIWindowSceneDelegate { var window: UIWindow? // Cold start via Universal Link func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { // Check if app was launched via a Universal Link if let userActivity = connectionOptions.userActivities.first, userActivity.activityType == NSUserActivityTypeBrowsingWeb, let url = userActivity.webpageURL { Task { await LinkrunnerSDK.shared.handleDeeplink(url: url.absoluteString) } } // Check if app was launched via a custom URL scheme if let urlContext = connectionOptions.urlContexts.first { Task { await LinkrunnerSDK.shared.handleDeeplink(url: urlContext.url.absoluteString) } } } // Warm start — Universal Links (app already running in background) func scene(_ scene: UIScene, continue userActivity: NSUserActivity) { guard userActivity.activityType == NSUserActivityTypeBrowsingWeb, let url = userActivity.webpageURL else { return } Task { await LinkrunnerSDK.shared.handleDeeplink(url: url.absoluteString) } } // Warm start — Custom URL schemes (app already running in background) func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) { guard let url = URLContexts.first?.url else { return } Task { await LinkrunnerSDK.shared.handleDeeplink(url: url.absoluteString) } }}
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}
Call setUserData each time the app opens and the user is logged in:
setUserData is optional and is not a replacement for signup. Always call signup first as soon as the user is identified (signup or login). Use setUserData afterwards only when additional user details become available later — for example, when the user adds a phone number, email, or completes their profile after identification.
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) }}
Revenue data is only stored and displayed for attributed users. Make sure you have implemented the .signup function before capturing payments. To attribute a test user, follow the Integration Testing guide. You can verify your events are being captured on the Events Settings page.
public enum PaymentType: String, Sendable { case firstPayment = "FIRST_PAYMENT" case secondPayment = "SECOND_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"}
public enum PaymentStatus: String, Sendable { case initiated = "PAYMENT_INITIATED" case completed = "PAYMENT_COMPLETED" case failed = "PAYMENT_FAILED" case cancelled = "PAYMENT_CANCELLED"}
Events are only stored and displayed for attributed users. Make sure you have implemented the .signup function before tracking events. To attribute a test user, follow the Integration Testing guide. You can verify your events are being captured on the Events Settings page. For capturing revenue, it is recommended to use the .capturePayment method instead of .trackEvent.
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) }}
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.
Minimum SDK Version: Ecommerce Event Manager requires linkrunner-iosv3.8.0 or above. Please ensure your SDK is updated before using this feature.
If you are tracking Ecommerce events to sync with Meta Catalog Sales, you must format your eventData to include Meta’s required fields. You also need to map your custom event to the standard commerce event in the Linkrunner Dashboard.For detailed explanations of the required fields like content_ids, contents, and value, refer to our Meta Commerce Manager documentation.
Select Identifiers under Certificates, IDs & Profiles.
Click on the app you want to track uninstalls for. Then, under Capabilities, search for Push Notifications and enable it.
Under Certificates, IDs & Profiles, select Keys and click on plus (+) icon to create a key. Enable APNs when creating the key and download the key file (p8).
The Key ID can be found in the Keys tab.
Bundle ID and Team ID:
Under Identifiers, click on your app and you will see the Bundle ID and Team ID (App ID Prefix).
Linkrunner Dashboard
In Linkrunner, go to Settings > Uninstall Tracking.
Under the iOS tab, upload the APNs Authentication Key (p8) file and enter the Key ID, Bundle ID and Team ID (App ID Prefix) that you copied from the Apple Developer Portal.
Integrate with Linkrunner SDK
Follow these instructions to integrate APNs with the Linkrunner SDK:
Set up Push Notifications:
Enable push notifications in your Xcode project by adding the Push Notifications capability.
Configure your app to provide the device’s APNs token to the Linkrunner SDK.
import UIKitimport Linkrunnerclass AppDelegate: UIResponder, UIApplicationDelegate { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { // Request push notification permissions UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .badge, .sound]) { granted, error in if granted { DispatchQueue.main.async { application.registerForRemoteNotifications() } } } return true } // Called when APNs token is received func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) { let tokenString = deviceToken.map { String(format: "%02.2hhx", $0) }.joined() Task { try? await LinkrunnerSDK.shared.setPushToken(tokenString) } } func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) { print("Failed to register for remote notifications: \(error)") }}