Skip to main content

iOS SDK Platform Research (2025/2026)

Research for the native OptoLink Swift iOS SDK (deep linking + deferred deep linking + attribution). Compiled from web sources, current as of 2026. Source URLs cited inline per finding.


AASA file + Associated Domains (unchanged core, stricter serving)​

Custom URL schemes — residual role only​

  • Custom schemes (myapp://) are trivially claimable by any app (no ownership verification), so Apple positions Universal Links as the primary mechanism; Universal Links "cannot be claimed by other apps." Source: https://blog.avinashkattamanchi.in/posts/ios-universal-links/
  • Branch (2026) still lists custom schemes as one of three link types but notes the failure mode: if the app isn't installed the user "sees an error or nothing happens." Their setup flow leads with Universal Links/AASA. Source: https://www.branch.io/resources/blog/how-to-set-up-deferred-deep-linking-on-ios/
  • Residual legit uses: opening your own app from your own app/webviews where a scheme is convenient, and legacy SDK callbacks. Any serious link platform treats schemes as a fallback, never the primary router.

2. Deferred deep linking on iOS — the hard problem​

No Play Install Referrer equivalent exists on iOS. Between tap → App Store → install → first launch, no official channel carries link context. Firebase Dynamic Links (the old free default) shut down Aug 25, 2025, so everything below is the current field. Source: https://nerdy.pro/blog/deferred-deep-linking-app-clips-install-referrer , https://firebase.google.com/support/dynamic-links-faq

2a. Clipboard token handoff (the workhorse)​

2c. Probabilistic / fingerprint matching — Apple's policy stance​

2d. App Clips as install bridge (deterministic, first-party)​

2e. AdServices / AdAttributionKit — paid UA vs organic​


3. Privacy compliance for SDKs​

ATT — when an attribution SDK must prompt​

Privacy manifests (PrivacyInfo.xcprivacy) + required-reason APIs​

SDK signing requirements (2024+)​


4. Distribution & modern Swift practice​

SPM is primary; CocoaPods declining​

Swift concurrency​

Minimum iOS version in 2025/2026​

SDK size expectations​

  • App Clips must stay ≤ 15 MB; app-extension-free SDKs should stay in the low single-digit MBs compressed — heavy-footprint SDKs (Firebase historically ~tens of MB) drive integrator pushback and App Size review complaints. lightweight is an explicit selling point in the category (DeepLinkNow markets "lightweight"; OptoLink's own node SDK is zero-dependency for the same reason). Sources: https://nerdy.pro/blog/deferred-deep-linking-app-clips-install-referrer , https://deeplinknow.com/docs/ios
  • Zero third-party dependencies in the SDK (avoid pulling in analytics/attribution transitive deps) — it protects integrators from privacy-manifest and signing conflicts of bundled third-party SDKs.

5. First-launch detection and device ID under ATT​

Identifier options​

IdentifierPersistenceATT neededNotes
IDFAPersistent-ish, user-resettableYes (any access)15–30% opt-in; mostly unavailable
IDFV (UIDevice.identifierForVendor)Same-vendor apps; changes to a new UUID when the last app of the vendor is uninstalledNoStable across reinstall only if another app from same vendor stays installed
Generated UUID in UserDefaultsCleared on uninstallNoSame-lifetime as app data — the honest "install ID"
Generated UUID in KeychainSurvives uninstall (iOS keeps the app's keychain items until device wipe); no API to delete on uninstallNoThe de-facto persistent device/install ID technique, widely documented
DeviceCheck / App AttestApple-backed per-device bits (2 bits!) / attestationNoFraud signals, not link identity

Sources: https://developer.apple.com/documentation/uikit/uidevice/identifierforvendor , https://stackoverflow.com/questions/21878560/how-to-preserve-identifierforvendor-in-ios-after-uninstalling-ios-app-on-device , https://stackoverflow.com/questions/3671499/iphone-keychain-items-persist-after-application-uninstall , https://anjay.sh/posts/ios-keychain/ , https://dev.classmethod.jp/en/articles/ios-device-identifiers-vs-android-ssaid/ , https://stackoverflow.com/questions/80005125/how-to-uniquely-identify-an-ios-device-and-persist-the-identifier-after-app-rein

First-launch detection​

  • First launch = absence of your own persisted install record. Store an install UUID on first initialize() (UserDefaults for the install-scoped ID; Keychain for the vendor-scoped persistent ID). No OS callback exists; isFirstLaunch is purely SDK-state.
  • Reinstall semantics drive attribution policy: UserDefaults ID dies with uninstall (fresh install → new user), Keychain ID survives (device returner). Use the Keychain ID to detect reinstalls for re-attribution/refraud, but declare its collection honestly in the privacy manifest and keep it first-party (never cross-app linked → no ATT).
  • IDFA handling pattern (Adjust-style): SDK reads ATTrackingManager.trackingAuthorizationStatus, includes IDFA only when .authorized, never requests the prompt itself unless the integrator opts in via config. Source: https://dev.adjust.com/en/sdk/ios/features/att/
  • Keychain caveat: items persisting past uninstall is expected Apple behavior (documented by Apple engineers) but some managed environments differ; treat Keychain-UUID as "highly persistent, not guaranteed eternal," and mirror the ID in DeviceCheck's 2 bits for hard fraud checks if needed. Sources: https://stackoverflow.com/questions/3671499/iphone-keychain-items-persist-after-application-uninstall , https://dev.classmethod.jp/en/articles/ios-device-identifiers-vs-android-ssaid/

  1. Universal Links are the only primary link mechanism (applinks: entitlement + AASA on the OptoLink short-link domain(s)); custom schemes are a documented fallback for webview-to-app hops only. The SDK ships docs for both AASA hosting (strict: HTTPS, application/json, no redirect, <128 KB) and entitlement setup.
  2. Document the Apple CDN lag: AASA changes propagate in hours via Apple's CDN; ship a ?mode=developer guidance section and an AASA validator/checklist in onboarding so customer integrations don't fail silently.
  3. Instruct integrators to handle BOTH delivery paths: .onOpenURL + .onContinueUserActivity(NSUserActivityTypeBrowsingWeb) feeding one OptoLink.handle(url:) entry point (and scene(_:continue:) for UIKit apps) — QR/cold-start Release builds demonstrably bypass .onOpenURL.
  4. Deferred deep linking: clipboard handoff is the core mechanism — short opaque token written by the click page, resolved server-side on first open. It survives Private Relay and matches Branch's current NativeLink direction.
  5. Use UIPasteboard.detectPatterns([.probableWebURL]) (iOS 16+) as the silent gate before any real pasteboard read — never read clipboard content on organic installs, never trigger the paste prompt without a probable match. On iOS 16+, offer an optional UIPasteControl-based "restore link" button (tap = consent, no prompt).
  6. Treat IP/cookie "Safari matching" as best-effort only and never implement fingerprint matching — Apple's privacy-manifest regime and iOS 26's Advanced Fingerprinting Protection make it a compliance liability; document a graceful degrade path (campaign-level attribution) when no match mechanism fires.
  7. Ship a PrivacyInfo.xcprivacy with the SDK from day one: NSPrivacyCollectedDataTypes (device ID, product interaction), UserDefaults reason CA92.1, and an explicit NSPrivacyTracking: false if we stay first-party-only (no IDFA, no cross-app linking) — that keeps customer apps un-prompted by ATT and review-clean.
  8. Offer optional IDFA support, opt-in by config: SDK reads ATT status, includes IDFA only when .authorized, never prompts on its own; document Guideline 5.1.2(i) risk for integrators who enable it.
  9. First-party session/install identifiers, not fingerprints: install UUID in UserDefaults (install-scoped) + vendor UUID in Keychain (device-scoped, survives reinstall) for re-attribution and fraud checks; both declared in the privacy manifest; no cross-app linking → no ATT requirement.
  10. Keep the SDK dependency-free and small (< a few MB; ideally source-distributed) — mirrors the zero-dependency Node SDK posture and avoids privacy-manifest/signing conflicts inside customer apps.
  11. Distribute via Swift Package Manager (source package as primary, signed XCframework binary target as optional artifact with checksum); skip CocoaPods or provide a stub podspec only if customers demand it.
  12. Modern Swift API surface: async/await initialize()/resolve() methods, an actor for SDK state and the pending-link queue, Sendable link-payload structs; maintain a lightweight completion-handler facade for pre-concurrency codebases if the min target requires it.
  13. Target iOS 15 as the deployment floor, #available(iOS 16, *) for paste-pattern/UIPasteControl features — covers ~96% of devices in 2026.
  14. Queue deferred links behind app readiness: persist the resolved payload and let the app decide when to route (post-onboarding/login) — the pending-link queue pattern; SDK exposes onDeferredLink once, apps gate navigation.
  15. Position AdServices/AdAttributionKit as paid-UA plumbing, not the product: organic/shared-link deferred context (our differentiator vs ad networks) comes from Universal Links + clipboard/App-Group handoff; if we later serve paid campaigns, integrate AAK postbacks server-side, not in the client SDK.
  16. Consider an optional App Clip companion target (customer-hosted, SDK-provided helper) as the deterministic premium path: clip writes the invocation URL to a shared App Group, full app consumes it on first launch — zero prompts, zero matching; document the ≤15 MB and separate-target trade-offs.