Smart Links logo

Smart Links

Developer Help
·

iOS Integration Overview

Adding the SDK to Your Xcode Project

The Smart Links iOS SDK is delivered as a Swift Package. Add it once to your project and it is available to all targets:

  1. In Xcode, choose File → Add Package Dependencies…
  2. Paste the repository URL into the search bar at the top right:
    https://github.com/rich-smartlynx/swift-framework
  3. Leave the version rule as Up to Next Major Version starting from 1.1.2 and click Add Package.
  4. In the package product chooser, make sure lynxapi is ticked and your app target is selected, then click Add Package again.

The package will appear under Package Dependencies in the Project Navigator. Xcode handles downloading, caching, and re-resolving the package automatically — there is nothing to add to a Podfile or Cartfile.

Initializing the Framework

Import lynxapi and create a single top-level Lynx instance with your API key. Your API key is shown in your Smart Links account settings. Declare it at file scope (outside any class or struct) so it is accessible from whichever lifecycle callback receives the link:

import lynxapi
let remote = Lynx(apiKey: "YOUR_API_KEY")

Calling checkForLink

remote.checkForLink(userActivity:completion:) is the single method you call in two situations:

  • On every app launch — pass nil for userActivity. The framework fingerprints the device and returns any pending deferred link from a previous web visit — even before the user has installed the app.
  • When a universal link opens the app — pass the NSUserActivity received from the system. The framework records the link for attribution tracking and returns it as a LinkResults.

The completion block is always called on a background thread — dispatch to DispatchQueue.main.async before touching UI.

LinkResults

The completion closure receives an optional LinkResults:

  • link: String — the full URL including all query parameters
  • confidence: Float — how certain the match is (see table below)
  • openCount: Int — number of times checkForLink has been called for this device
  • linkTypenone, universal, deferred, previous_deferred, or previous_universal. If no link is present on this open but the device was previously seen with one, the last known link is returned.

Confidence Values

Confidence Meaning
100 Universal link — opened directly from a tapped link, no matching uncertainty.
99.99 Deferred link with virtually no chance of returning the wrong link.
≥ 90 Very high probability the returned link is the correct one.
50 A link was found but there is only a 50 % chance it is the correct match — treat with caution.
0 No link found. The user did not arrive via a Smart Link — this is normal, not an error.

Attribution Tracking

Any query parameter whose name starts with utm_ is automatically recorded as an attribution parameter. No extra setup is needed — just include them in your Smart Links (e.g. ?utm_source=newsletter&utm_campaign=spring) and they will appear in your dashboard's Attribution Links section.

Entitlements & Associated Domains

iOS only opens a universal link in your app if two things are true: your app declares it can handle the domain, and the server at that domain publishes a file that names your app. Smart Links handles the server side automatically — you just need to configure Xcode.

Step 1 — Add the Associated Domains capability in Xcode:

  1. Open your project in Xcode and select the project file in the Navigator (the blue icon at the top).
  2. Select your app target from the target list on the left.
  3. Click the Signing & Capabilities tab.
  4. Click + Capability (top-left of the tab) and double-click Associated Domains.
  5. A new Associated Domains section appears. Click the + button inside it and add:
    applinks:apps.smartlinks.mobi

Xcode writes this entry into your target's .entitlements file automatically. You do not need to edit the entitlements file by hand.

Step 2 — Nothing. The AASA file is already there.

iOS requires the server to host an apple-app-site-association (AASA) file at https://apps.smartlinks.mobi/.well-known/apple-app-site-association. This file lists which app bundle IDs and Team IDs are authorised to handle links on that domain. Smart Links generates and hosts this file automatically the moment you create your account — it is updated whenever you change your iOS bundle identifier or Team Identifier in your account settings. There is nothing to upload or configure on the server side.

When your app is installed and iOS sees a link to apps.smartlinks.mobi, it fetches the AASA file in the background, finds your bundle ID and Team ID in it, and from that point on routes matching links directly into your app instead of Safari.

Shared: UniversalLinkRouter.swift

Drop this file into any of the four architecture patterns — the entry-point code is the only thing that changes between them.


          

Architecture Examples

Choose the pattern that matches your app's lifecycle. The link-handling logic is the same in every case — only the entry point differs.

A complete, buildable Xcode project covering all four patterns is available at github.com/rich-smartlynx/universal-links-demo.


          

Testing

Universal Links on a Physical Device

Build and install your app, then test a universal link by tapping it from outside your app — Safari, Notes, Mail, or Messages all work. Typing a URL directly into Safari's address bar will not trigger a universal link; iOS only intercepts links that are tapped, not navigated to manually.

When the link fires correctly a banner appears briefly at the top of Safari before the app opens. If the banner doesn't appear and Safari loads the page instead, see Troubleshooting below.

Universal Links in the Simulator

The Simulator supports universal links via the simctl command-line tool. Run this from Terminal — no physical device needed:

xcrun simctl openurl booted "https://apps.smartlinks.mobi/basename?param=value"

Replace basename with your app's base name and add any query parameters you want to test. The Simulator must already have your app installed.

Testing Deferred Links

A deferred link fires when checkForLink(userActivity: nil) is called on launch and the framework finds a fingerprint match from a previous web visit. To set one up:

  1. Uninstall your app from the device or Simulator (this clears any existing state).
  2. Open Safari and visit your Smart Link URL (e.g. https://apps.smartlinks.mobi/basename?utm_source=test). Safari (or the device's default browser) will capture a fingerprint and then redirect the user to the App Store, already pointing to your app. If the app is already installed, this same link will act as a universal link and open the app directly instead.
  3. Install and launch your app. The call to checkForLink(userActivity: nil) in didFinishLaunchingWithOptions (or .task in SwiftUI) will return the link with a confidence value.

You can repeat this cycle as many times as needed — just uninstall between runs to reset the deferred link state. Each Simulator device has its own independent fingerprint, so you can run multiple scenarios in parallel using different Simulator devices.

Checking the Confidence Value

During development, log the full LinkResults so you can see exactly what the framework returns:


          

Troubleshooting

Link opens in Safari instead of the app

This is the most common issue. Work through these checks in order:

  • Entitlement not saved. Open Xcode → Signing & Capabilities and confirm applinks:apps.smartlinks.mobi is present. It is easy to add it and accidentally close Xcode without the file being written to disk — check that your .entitlements file is committed to source control and contains the entry.
  • Bundle ID or Team ID mismatch. The bundle ID and Team ID in your Smart Links account settings must exactly match what is in your Xcode target. A mismatch means the AASA file won't list your app and iOS will fall back to Safari.
  • App not installed. Universal links only work when the app is already on the device. If the app is not installed, the link opens in Safari — this is expected iOS behaviour and is precisely why deferred links exist.
  • Link tapped from address bar. Typing or pasting a URL into Safari's address bar bypasses universal link routing. The link must be tapped from somewhere — Notes, Mail, Messages, another app, or a web page.
  • AASA cache. iOS fetches the AASA file via Apple's own CDN, not directly from the server, and the CDN can cache it for several hours. If you recently updated your bundle ID or Team ID, delete the app and reinstall to force iOS to re-validate the entitlement against the latest AASA.

checkForLink always returns nil or confidence 0

  • Wrong API key. Double-check that the key in your code matches the one shown in your Smart Links account settings exactly — it is case-sensitive.
  • No prior web visit. For a deferred link to be returned, someone must have visited a Smart Link URL from this device before the app was installed. If you are testing on a fresh Simulator or device without doing the Safari-visit step first, confidence 0 is the correct result.
  • Network not available. checkForLink makes a network call. Confirm the device or Simulator has connectivity and that no firewall or VPN is blocking apps.smartlinks.mobi.

Cold launch works but warm resume doesn't (or vice versa)

  • SceneDelegate pattern — missing one of the two entry points. With SceneDelegate you must implement both scene(_:willConnectTo:options:) (cold launch) and scene(_:continue:) (warm/background resume). Implementing only one means you will miss links in the other scenario.
  • AppDelegate pattern with SceneDelegate present. If your Info.plist contains a scene configuration, iOS routes universal links to the SceneDelegate and application(_:continue:restorationHandler:) in the AppDelegate is never called. Remove the scene configuration or switch to the SceneDelegate pattern.

Universal links tapped inside the app open in Safari

This is intentional iOS behaviour — tapping a universal link within the same app (e.g. in a WKWebView or SFSafariViewController) always opens in Safari. If you need in-app link handling, intercept the URL in the web view's navigation delegate and call checkForLink directly rather than letting the system open it.

Everything looks correct but still not working

Apple provides a built-in diagnostic tool. On a physical device running iOS 16.4 or later, go to Settings → Developer → Universal Links → Diagnostics and enter your URL. It will tell you exactly why the link is or isn't being claimed by your app — including whether the AASA was fetched, whether the entitlement matched, and which app won the claim.