Mobile
iOS SDK
This page is assembled from
sdks/ios/README.mdin the repository. See the SDK overview for the choice between native SDKs and wrappers.
Formerly the DTrackier SDK. Until 2.0 the deprecated
DTrackier/DTrackierCoreSwiftPM products and theDTrackiertype alias keep pre-rename code compiling (import DTrackier,DTrackier.initialize); switch to theMetrixedproduct andimport Metrixed.
Swift 5.9+, iOS 13+, no third-party dependencies. Two modules:
MetrixedCore: platform-agnostic engine (offline queue, HMAC request signing, payloads, consent, conversion-value evaluation). Builds and is tested on Linux.Metrixed: the public API plus the iOS integration (ATT, IDFA/IDFV, AdServices, SKAdNetwork / AdAttributionKit, Universal Links, LinkMe, StoreKit 2, push token). Re-exports the core types.
Install
Swift Package Manager (the package lives in sdks/ios and is published to a mirror repo, tag ios-vX.Y.Z):
.package(url: "https://github.com/metrixed/metrixed-ios.git", from: "1.0.0")
CocoaPods: pod 'Metrixed' (depends on MetrixedCore).
Initialise
import Metrixed
// application(_:didFinishLaunchingWithOptions:)
var config = DTConfig(
appToken: "APP_TOKEN",
environment: .production, // .sandbox: tagged, excluded from billing
baseURL: URL(string: "https://sdk.yourdomain.com")!, // measurement host (never receives the IDFA)
trackingBaseURL: URL(string: "https://track.yourdomain.com")!, // IDFA requests only, after ATT authorization
sdkSecret: "SDK_SECRET") // request signing (required when the app enforces signatures)
config.attWaitInterval = 30 // seconds to hold the install request for the ATT answer (0 = don't wait, max 360)
Metrixed.initialize(config: config)
Requests are signed X-Dt-Signature = hex(HMAC-SHA256(secret, timestamp + "." + body)) with a fresh timestamp at send
time. Queued requests survive restarts and are retried with exponential backoff (max 15 min); events older than 58 days
are dropped. A 401 with a Date header corrects the device clock offset and retries once.
| Option | Default | Notes |
|---|---|---|
sessionTimeout | 30 min | new session after this idle time |
coppaCompliant | false | kids mode: no IDFA/IDFV/AdServices/pasteboard/SKAN |
readIDFA, readIDFV | true | opt-outs for id reads |
externalDeviceId | nil | your own id, sent as device.external_device_id |
linkMeEnabled, linkMeHosts | false, [] | pasteboard deferred deep link (opt-in) |
resolveDeferredDeepLinks | true | server lookup after the first install |
skanEnabled, postbackFramework | true, .both | conversion-value updates from the server schema |
storeKitForwarding | false | forward StoreKit 2 transactions as purchase events |
callbackQueue | main | queue for deep link callbacks |
Events
Metrixed.trackEvent("level_up", callbackParameters: ["level": "5"])
var e = DTEvent(name: "purchase")
e.setRevenue(Decimal(string: "4.99")!, currency: "USD")
e.dedupeId = order.id // idempotency: retries and replays count once
e.addPartnerParameter("af_content_id", "sku1")
Metrixed.trackEvent(e)
Metrixed.adRevenue(DTAdRevenue(source: "applovin_max", revenue: 0.012, currency: "USD", network: "AppLovin"))
Metrixed.setCustomerUserId("user-42")
Revenue is sent as a 6-decimal string; an event with revenue needs a currency.
Deep links
Metrixed.setDeeplinkHandler { link in // direct, Universal Link, LinkMe and deferred links
route(link.deepLinkValue ?? link.path, params: link.query)
}
// URL schemes: application(_:open:options:) -> Metrixed.processDeeplink(url)
// Universal Links: application(_:continue:...) -> Metrixed.continueUserActivity(activity)
// SwiftUI: .onOpenURL { Metrixed.processDeeplink($0, source: .universalLink) }
Add applinks:go.yourdomain.com to Associated Domains; Metrixed serves the apple-app-site-association file for
branded unilink domains. Opening a link with a click id re-attributes (re-engagement). On the first launch the install
response carries deep_link_value when the click matched; otherwise the SDK asks POST /sdk/v1/deeplink (WP-27) and with
linkMeEnabled also reads a unilink URL from the pasteboard (iOS 16+ may show the system paste prompt).
SKAdNetwork / AdAttributionKit
- Add the postback endpoints to
Info.plist:NSAdvertisingAttributionReportEndpointand, for AdAttributionKit,AdAttributionKitReportEndpoint= your Metrixed SKAN host. - The SDK downloads the per-app conversion-value schema (
GET /sdk/v1/config, cached 6 h), evaluates it on every event and callsSKAdNetwork.updatePostbackConversionValue(iOS 16.1+),updateConversionValue(iOS 14-16.0) andAdAttributionKitPostback.updateConversionValue(iOS 17.4+). Values only increase, window 0 sends fine + coarse, windows 1-2 coarse only. Schema format and vectors:sdks/test-vectors/cv-schema.json.
ATT and IDFA
Add NSUserTrackingUsageDescription. Show the prompt yourself or call Metrixed.requestTrackingAuthorization. The IDFA is
read only when ATT is authorized (or iOS < 14 with Limit Ad Tracking off), sharing consent is not revoked, and
coppaCompliant is off. Without it the SDK still sends the IDFV and the AdServices token (Apple Search Ads).
Consent and privacy
Metrixed.setConsent(DTConsent(thirdPartySharing: false, measurement: true, tcf: tcString,
googleDMA: .init(eea: true, adUserData: false, adPersonalization: false)))
Metrixed.setThirdPartySharing(false) // drops the IDFA from payloads; server stops forwarding ids to partners
Metrixed.gdprForgetMe() // server erase + wipes local data; the SDK stays disabled afterwards
measurement: false pauses all tracking. The consent object rides on every payload. PrivacyInfo.xcprivacy ships with the
package: it declares device id and product interaction data, NSPrivacyTracking = true, and lists the tracking host
track.metrixed.com. If you use a custom trackingBaseURL, add that host to NSPrivacyTrackingDomains in your app's own
manifest; the measurement host must not be listed (it carries no tracking identifiers).
Push token (uninstall measurement)
Metrixed.setPushToken(deviceToken) from didRegisterForRemoteNotificationsWithDeviceToken.
StoreKit 2
config.storeKitForwarding = true forwards verified Transaction.updates as purchase events (idempotency key
sk2_<transactionId>, signed JWS attached as partner param signed_transaction). For purchases you handle yourself call
Metrixed.trackTransaction(result). The SDK never finishes transactions.
Sandbox
environment: .sandbox tags every payload; sandbox traffic is excluded from billing and reports unless toggled.
Testing
make sdk-ios-test # Linux core + facade tests in a pinned swift:5.10.1 image, incl. shared signing and CV vectors
make sdk-ios-e2e # same SDK against a real ingest process (signed requests, dedupe, erase)
Everything under #if os(iOS) (ATT, SKAN, AAK, UIKit lifecycle, StoreKit, AdServices, pasteboard) only compiles on macOS:
see .github/workflows/sdk-ios.yml (macOS runner: xcodebuild device build, simulator tests, sample app, pod lib lint).
Sample app: sample/ (XcodeGen project.yml).