iOS
admin
MetaRouter iOS SDK
Capture behavioral data directly from your iOS app and route it through MetaRouter to any destination — without juggling multiple vendor SDKs.
Why use the Mobile SDK?
One integration, unlimited destinations
Stop embedding separate SDKs for analytics, marketing, and data warehouses. Collect once, route everywhere through MetaRouter.
Update routing without app releases
Add or remove destinations server-side. No code changes, no App Store review cycles.
Reliable delivery, even offline
Events are queued locally and delivered automatically when connectivity returns. Built-in retry logic handles transient failures. Offline mode supported for durable event persistence regardless of network conditions.
Automatic identity resolution
Track users from first app open through sign-up and beyond. The SDK manages anonymous IDs, persists identity across sessions, and connects pre-login activity to known users.
Privacy-ready by design
Built-in support for IDFA consent via App Tracking Transparency, easy opt-out handling, and clean user data reset for GDPR/CCPA compliance.
Lightweight footprint
Zero external dependencies. Minimal impact on app size and startup time.
Quick start
1) Add the SDK
Add the package via Swift Package Manager in Xcode:
File → Add Package Dependencies
Enter:
https://github.com/metarouterio/ios-sdk.git
Or add it directly to your Package.swift:
dependencies: [
.package(url: "https://github.com/metarouterio/ios-sdk.git", from: "1.5.0")
]2) Initialize
:
import MetaRouter
// Call once at app launch (e.g. in AppDelegate or your App init).
// No need to capture return value
MetaRouter.Analytics.initialize(
with: InitOptions(
writeKey: "YOUR_WRITE_KEY",
ingestionHost: "https://YOUR_CLUSTER.YOUR_SITE.com"
)
)Your writeKey and ingestionHost are available in the MetaRouter dashboard.
Accessing the client:
After initialization, access the SDK anywhere through the shared instance:
MetaRouter.Analytics.shared.track("Order Completed")
MetaRouter.Analytics.shared.identify("user-123")initialize(with:) returns immediately and binds the underlying client in the background; any calls made before binding completes are queued and replayed, so shared is safe to use right away. You don't need to store or inject the returned reference.
3) Start tracking
// Track user actions
analytics.track("Product Viewed", properties: [
"sku": "SHOE-123",
"price": 89.99
])
// Identify known users
analytics.identify("user-456", traits: [
"email": "[email protected]",
"plan": "premium"
])
// Track screen views
analytics.screen("Product Detail", properties: [
"category": "Footwear"
])Events are batched and delivered automatically. No additional setup required.
Core capabilities
Event tracking
| Method | Purpose |
|---|---|
track(_:properties:) | Capture user actions — purchases, clicks, feature usage |
screen(_:properties:) | Record screen views for navigation analytics |
page(_:properties:) | Record page views (web semantics, if applicable) |
User identity
| Method | Purpose |
|---|---|
identify(_:traits:) | Associate events with a known user |
group(_:traits:) | Associate users with a company, team, or account |
alias(_:) | Link anonymous activity to a newly identified user |
getAnonymousId() | Returns the anonymous ID the SDK assigned to this device |
Lifecycle & privacy
| Method | Purpose |
|---|---|
flush() | Send queued events immediately |
reset() | Clear all user data (use on logout) |
setAdvertisingId(_:) | Set IDFA for attribution (with user consent) |
clearAdvertisingId() | Remove IDFA when user opts out |
Identity that just works
The SDK automatically handles the complexity of user identity:
Before login — Users are assigned a stable anonymous ID
On login — Call identify() to attach a known user ID
Across sessions — Identity persists through app restarts
On logout — Call reset() to clear everything and start fresh
Connect anonymous browsing to authenticated users with alias():
// User browses anonymously, then signs up
analytics.track(
"Product Viewed",
properties: ["sku": "ABC"]
) // tracked as anonymous
// User creates account
analytics.alias("new-user-789") // links anonymous → known
analytics.identify(
"new-user-789",
traits: ["email": "[email protected]"]
)
// Full journey is now connected in your downstream toolsConfiguration Options
Use InitOptions to configure delivery cadence, logging, in-memory buffering, and disk-backed persistence.
let analytics = MetaRouter.Analytics.initialize(
with: InitOptions(
writeKey: "YOUR_WRITE_KEY", // Required
ingestionHost: "https://YOUR_CLUSTER.mr-in.com", // Required
flushIntervalSeconds: 10, // Optional (default: 10)
debug: false, // Optional (default: false)
maxQueueEvents: 2000, // Optional (default: 2000)
maxDiskEvents: 10000 // Optional (default: 10000)
)
)| Option | Required | Default | Description |
|---|---|---|---|
writeKey | Yes | — | Your MetaRouter write key. Must not be empty. |
ingestionHost | Yes | — | Your MetaRouter ingestion endpoint. Pass either a String or URL; do not include a trailing slash. |
flushIntervalSeconds | No | 10 | How often the SDK attempts to send queued events. Values below 1 are clamped to 1. |
debug | No | false | Enables verbose SDK logging for troubleshooting. |
maxQueueEvents | No | 2000 | Maximum events held in the in-memory queue. Values below 1 are clamped to 1. |
maxDiskEvents | No | 10000 | Maximum unsent events retained on disk for crash safety and offline recovery. Set to 0 to disable disk persistence. |
trackLifecycleEvents | No | false | Automatically emit the four Application * lifecycle events. |
Lifecycle Events
Turn on automatic tracking of the four standard application-lifecycle events — Application Installed, Application Updated, Application Opened, and Application Backgrounded — to anchor attribution, retention, and session reporting without instrumenting each one by hand. They flow through the same pipeline as your own track calls.
Enable it
Lifecycle events are opt-in. Set trackLifecycleEvents: true in InitOptions:
let analytics = MetaRouter.Analytics.initialize(
with: InitOptions(
writeKey: "your-write-key",
ingestionHost: "https://your-ingestion-endpoint.com",
trackLifecycleEvents: true
)
)With the flag left at its default (false), the SDK emits none of these events and recordOpenedURL(...) is ignored. Upgrading the SDK never turns them on silently — an app has to opt in.
The events
| Event | When it fires | Properties |
|---|---|---|
Application Installed | First launch after a fresh install (no prior identity or lifecycle state on the device) | version, build |
Application Updated | First launch after the app's version/build changes — also the first launch after upgrading from a pre-lifecycle SDK build | version, build, previous_version, previous_build ("unknown" for the SDK-upgrade case) |
Application Opened | Cold launch while active, and each background → active return | version, build, from_background (false on cold launch, true on return), plus url / referring_application when a deep link was recorded |
Application Backgrounded | App enters the background | — |
On cold launch, Application Installed/Updated fire before Application Opened, so attribution sees the install or update ahead of the session start. Foreground and background transitions come from the app's own didBecomeActive / didEnterBackground notifications. Only a full background → active return emits Application Opened — brief interruptions that leave the app merely inactive (Control Center, a system prompt, Face ID) do not.
If the process is launched into the background (silent push, background fetch), the cold-launch Application Opened is deferred until the first real activation, then emitted with from_background: false.
Install/update state is stored per-device (in UserDefaults, in a namespace separate from identity) — calling reset() clears the user but keeps the install/update history.
Deep links
The SDK doesn't auto-capture deep links; you forward the URL so it can be attached to the next Application Opened event.
| Method | Purpose |
|---|---|
recordOpenedURL(_ url: URL, sourceApplication: String?) | Record a deep-link URL to attach to the next Application Opened event. |
Call it from wherever your app receives URLs — application(_:open:options:), scene(_:openURLContexts:), and application(_:didFinishLaunchingWithOptions:) for cold-launch capture:
// AppDelegate — cold launch
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil
) -> Bool {
if let url = launchOptions?[.url] as? URL {
let source = launchOptions?[.sourceApplication] as? String
MetaRouter.Analytics.shared.recordOpenedURL(url, sourceApplication: source)
}
return true
}
// AppDelegate — while running
func application(
_ app: UIApplication,
open url: URL,
options: [UIApplication.OpenURLOptionsKey: Any] = [:]
) -> Bool {
MetaRouter.Analytics.shared.recordOpenedURL(
url,
sourceApplication: options[.sourceApplication] as? String
)
return true
}Using scenes:
func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
guard let context = URLContexts.first else { return }
MetaRouter.Analytics.shared.recordOpenedURL(
context.url,
sourceApplication: context.options.sourceApplication
)
}The next Application Opened the SDK emits carries url (and referring_application when you pass a source). The buffer is one-shot — cleared once that event fires — and last-write-wins if you record more than one URL before it.
Deep-link URLs often carry secrets — auth tokens, OTPs, magic-link codes. The SDK forwards the URL as-is and does not sanitize it. Strip sensitive query parameters before callingrecordOpenedURL(...).
Advertising & attribution
For ad attribution, you can include the Identifier for Advertisers (IDFA):
import AppTrackingTransparency
import AdSupport
// Request permission (required on iOS 14.5+)
ATTrackingManager.requestTrackingAuthorization { status in
if status == .authorized {
let idfa = ASIdentifierManager.shared()
.advertisingIdentifier
.uuidString
analytics.setAdvertisingId(idfa)
}
}// When user opts out
analytics.clearAdvertisingId()Privacy note:
iOS requires explicit user consent via App Tracking Transparency before collecting the IDFA.
Add NSUserTrackingUsageDescription to your Info.plist with a clear explanation of why you’re requesting tracking permission.
See the GitHub docs for full implementation details:
https://github.com/metarouterio/ios-sdk
Requirements
| Component | Version |
|---|---|
| iOS | 15.0+ |
| macOS | 12.0+ |
| Swift | 6.1+ |
Updated 18 days ago