iOS SDK

Install and integrate the LinkTrail iOS SDK — module LinkTrailSDK, iOS 15+.

Binary XCFramework · module LinkTrailSDK · entry point LinkTrail · iOS 15+, Swift 5.9 / Xcode 15+ · Swift Package Manager or CocoaPods.

Install

Swift Package Manager

In Xcode: File → Add Package Dependencies… → paste the repo URL → pick a version. Or in a Package.swift:

Package.swiftswift
.package(url: "https://github.com/linktrail-io/ios-sdk.git", from: "0.0.8")

CocoaPods

Podfileruby
pod 'LinkTrailSDK', '~> 0.0.8'

Then run pod install and open the generated .xcworkspace.

Integrate

import LinkTrailSDK

// At launch (SwiftUI App.init or AppDelegate). The API key is required — configure throws.
// The install is tracked automatically.
try LinkTrail.configure(apiKey: "lt_live_...")

// One hook — fires for deferred (first-launch) AND re-engagement links:
LinkTrail.shared?.onLink { link, source in
    router.route(to: link.path, customData: link.customData)
}

LinkTrail.shared?.onError { error in /* e.g. LinkTrailError.invalidAPIKey */ }

Forward incoming links — SwiftUI:

.onOpenURL { LinkTrail.shared?.handleDeepLink($0) }
.onContinueUserActivity(NSUserActivityTypeBrowsingWeb) { activity in
    if let url = activity.webpageURL { LinkTrail.shared?.handleDeepLink(url) }
}

Or UIKit, from your SceneDelegate:

SceneDelegate.swiftswift
// Universal Links (app already running or cold-started from a link):
func scene(_ scene: UIScene, continue userActivity: NSUserActivity) {
    if let url = userActivity.webpageURL { LinkTrail.shared?.handleDeepLink(url) }
}

// Custom schemes (yourapp://…):
func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
    URLContexts.forEach { LinkTrail.shared?.handleDeepLink($0.url) }
}

handleDeepLink returns false for URLs that aren't LinkTrail links, so you can fall through to your own routing. Every callback API also has an async throws twin (e.g. try await LinkTrail.shared?.handleDeepLink(url) returns the resolved link). Callbacks are delivered on the main thread.

More

// Custom + revenue events:
LinkTrail.shared?.trackEvent(name: "purchase", value: 59.99, currency: "USD")

// Cached results:
let attribution = LinkTrail.shared?.lastAttribution
let lastLink = LinkTrail.shared?.lastDeepLink

// Attribution hook (fires when an install is attributed):
LinkTrail.shared?.onAttribution { attribution in /* … */ }

// Consent-gated install (defer configure's auto-track, then call manually):
let lt = try LinkTrail.configure(apiKey: "lt_live_...",
                                 options: LinkTrailOptions(autoTrackInstall: false))
lt.trackInstall()

// ATT / SKAdNetwork:
await LinkTrail.shared?.requestTrackingAuthorization()
LinkTrail.shared?.registerForSKAdAttribution()
LinkTrail.shared?.updateConversionValue(42, coarseValue: .medium)
Calling requestTrackingAuthorization() requires NSUserTrackingUsageDescription in your Info.plist — without it the ATT prompt never appears and authorization is denied.

Options & defaults

LinkTrailOptions fieldDefaultNotes
logEnabledfalseMaster switch for SDK logging
logLevel.info.debug · .info · .warning · .error · .none
requestTimeout15 sPer-request network timeout
retryPolicy.default3 attempts, 0.5 s base / 8 s max backoff; .disabled turns retries off
linkDomains[] (all hosts)See the warning under Deep-link setup before setting this
autoTrackInstalltruefalse = consent-gated; call trackInstall() yourself

API surface

APIPurpose
configure(apiKey:options:)Initialize; auto-tracks the install
onLink { link, source }Deep link delivered (deferred + re-engagement)
onAttribution { attribution }Install attribution result
onError { error }Failures (invalid key, network…)
handleDeepLink(url)Forward Universal Links / custom schemes
trackEvent(name:value:currency:)Custom + revenue events
trackInstall(force:)Manual install track (with autoTrackInstall: false)
lastAttribution / lastDeepLinkCached last results
requestTrackingAuthorization()ATT prompt wrapper
registerForSKAdAttribution() / updateConversionValue(_:coarseValue:)SKAdNetwork

Add the Associated Domains capability with applinks:yourapp.linktrail.io. For a custom scheme, add it under CFBundleURLTypes in Info.plist.

The AASA file is generated and hosted by LinkTrail — it's built from the Team ID + bundle ID you registered in the dashboard. Nothing to upload. If you set linkDomains in LinkTrailOptions, list every link host: with a non-empty list, re-engagement opens only route for listed hosts (deferred install links still route, so the bug hides on fresh installs). Leave it empty to handle every parseable link.

App Store privacy

The SDK ships its own privacy manifest (PrivacyInfo.xcprivacy), so Xcode's privacy report includes it automatically. What it declares: a per-install device identifier (IDFV, with a UUID fallback) linked to the user, used for Analytics and App Functionality; no IDFA collection; no cross-company tracking (NSPrivacyTracking = false). In App Store Connect's App Privacy questionnaire, count LinkTrail as: Identifiers → Device ID, linked to the user, not used for tracking.

Example app: KickFlip — a SwiftUI storefront in example/ wired end to end. cd example && xcodegen generate && open KickFlipDemo.xcodeproj.