Skip to content
Docs

Mobile

iOS SDK

This page is assembled from sdks/ios/README.md in the repository. See the SDK overview for the choice between native SDKs and wrappers.

Formerly the DTrackier SDK. Until 2.0 the deprecated DTrackier / DTrackierCore SwiftPM products and the DTrackier type alias keep pre-rename code compiling (import DTrackier, DTrackier.initialize); switch to the Metrixed product and import 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):

swift
.package(url: "https://github.com/metrixed/metrixed-ios.git", from: "1.0.0")

CocoaPods: pod 'Metrixed' (depends on MetrixedCore).

Initialise

swift
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.

OptionDefaultNotes
sessionTimeout30 minnew session after this idle time
coppaCompliantfalsekids mode: no IDFA/IDFV/AdServices/pasteboard/SKAN
readIDFA, readIDFVtrueopt-outs for id reads
externalDeviceIdnilyour own id, sent as device.external_device_id
linkMeEnabled, linkMeHostsfalse, []pasteboard deferred deep link (opt-in)
resolveDeferredDeepLinkstrueserver lookup after the first install
skanEnabled, postbackFrameworktrue, .bothconversion-value updates from the server schema
storeKitForwardingfalseforward StoreKit 2 transactions as purchase events
callbackQueuemainqueue for deep link callbacks

Events

swift
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.

swift
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

  1. Add the postback endpoints to Info.plist: NSAdvertisingAttributionReportEndpoint and, for AdAttributionKit, AdAttributionKitReportEndpoint = your Metrixed SKAN host.
  2. The SDK downloads the per-app conversion-value schema (GET /sdk/v1/config, cached 6 h), evaluates it on every event and calls SKAdNetwork.updatePostbackConversionValue (iOS 16.1+), updateConversionValue (iOS 14-16.0) and AdAttributionKit Postback.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).

swift
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).