Requirements
- iOS 15.0 or higher
- Swift 5.9 or higher
- Xcode 14.0 or higher
Installation
Swift Package Manager
The Linkrunner SDK can be installed via Swift Package Manager (SPM), which is integrated directly into Xcode.
- In Xcode, select File → Add Package Dependencies…
- Enter the following repository URL:
https://github.com/linkrunner-labs/linkrunner-ios.git
- Select the version you want to use (we recommend using the latest version)
- Click Add Package
- Choose the library type LinkrunnerStatic
Alternatively, you can add the package dependency to your Package.swift file:
dependencies: [
.package(url: "https://github.com/linkrunner-labs/linkrunner-ios.git", from: "3.0.2")
]
And add the dependency to your target:
targets: [
.target(
name: "YourApp",
dependencies: [
.product(name: "Linkrunner", package: "linkrunner-ios")
]
)
]
Importing in Swift
After installation, you can import the SDK in your Swift files:
Required Permissions
App Tracking Transparency
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>
SKAdNetwork Configuration
To enable SKAdNetwork postback copies to be sent to Linkrunner, add the following keys to your Info.plist file:
<key>NSAdvertisingAttributionReportEndpoint</key>
<string>https://linkrunner-skan.com</string>
<key>AttributionCopyEndpoint</key>
<string>https://linkrunner-skan.com</string>
For complete SKAdNetwork integration details, see the SKAdNetwork Integration Guide.
Network Access
The SDK requires network access to communicate with Linkrunner services. Make sure your app has the appropriate permissions for network access.
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",
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()
}
}
}
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
disableIdfa (optional): Boolean flag to disable IDFA collection (defaults to false)
debug (optional): Boolean flag to enable debug mode for development (defaults to false)
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: "[email protected]", // 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: "[email protected]" // 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 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"
}
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
| Function | Where to Place | When to Call |
|---|
LinkrunnerSDK.shared.initialize | App initialization | Once when app starts |
LinkrunnerSDK.shared.getAttributionData | Attribution data handling flow | Whenever the attribution data is needed |
LinkRunner.getInstance().setAdditionalData | Integration code | When third-party integration IDs are available |
LinkrunnerSDK.shared.signup | Onboarding flow | Once after user completes onboarding |
LinkrunnerSDK.shared.setUserData | Authentication logic | Every time app opens with logged-in user |
LinkrunnerSDK.shared.trackEvent | Throughout app | When specific user actions occur |
LinkrunnerSDK.shared.capturePayment | Payment processing | When user makes a payment |
LinkrunnerSDK.shared.removePayment | Refund flow | When 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()
}
}
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)
}
}
}
Next Steps
Support
If you encounter issues during integration, contact us at [email protected].