Universal links for iOS SDK
Enable and track universal links in your app using the iOS SDK
Universal links allow the links you send through Marketing to open directly in your native mobile application without any redirects that would hinder your users' experience.
For details on how universal links work and how they can improve your users' experience, refer to the Universal links section in the Campaigns documentation.
This page describes the steps required to support and track incoming universal links in your app using the iOS SDK.
Enable universal links
To support universal links in your app, you must create a two-way association between your app and your website and specify the URLs that your app handles.
Follow the instructions in Supporting associated domains in the Apple Developer documentation.
- Ensure you have added the Associated Domains Entitlement to your application target's
Associated Domainson theSigning & Capabilitiestab, for example:applinks:example.com webcredentials:example.com - Ensure you have set up the
apple-app-site-associationfile on your website and that it lists the app identifiers for your domain in theapplinksservice. For example:
The file must be available on a URL matching the following format.{ "applinks": { "apps": [], "details": [ { "appID": "ABCDE12345.com.example.ExampleApp", "paths": [ "/engagement/*", "/*" ] } ] } }https://<fully qualified domain>/.well-known/apple-app-site-association
Once the above items are in place, opening universal links should open your app.
The easiest way to test the integration is to send yourself an email containing a universal link and open it in your email client in a web browser. Universal links work correctly when a user taps or clicks a link to a different domain. Copy-pasting the URL into Safari doesn't work, neither does following a link to the current domain, or opening the URL with Javascript.
Track universal links
When the system opens your app after a user taps or clicks on a universal link, your app receives an NSUserActivity object with an activityType value of NSUserActivityTypeBrowsingWeb. You must forward that activity to the SDK so campaign clicks are tracked in Marketing.
The activity object's webpageURL property contains the URL passed to campaign-click tracking.
UIScene lifecycle (recommended for iOS 27+)
If your app uses UIApplicationSceneManifest, UIKit delivers universal links to your SceneDelegate, not AppDelegate.application(_:continue:restorationHandler:).
Subclass ExponeaSceneDelegate in your scene delegate and call super from the scene lifecycle methods:
import ExponeaSDK
class SceneDelegate: ExponeaSceneDelegate {
var window: UIWindow?
override func scene(
_ scene: UIScene,
willConnectTo session: UISceneSession,
options connectionOptions: UIScene.ConnectionOptions
) {
super.scene(scene, willConnectTo: session, options: connectionOptions)
// cold launch: connectionOptions.userActivities
}
override func scene(_ scene: UIScene, continue userActivity: NSUserActivity) {
super.scene(scene, continue: userActivity)
// warm launch
}
}For a fully custom SceneDelegate, forward universal links manually:
func scene(_ scene: UIScene, continue userActivity: NSUserActivity) {
Exponea.shared.handleUniversalLink(userActivity)
}
func scene(
_ scene: UIScene,
willConnectTo session: UISceneSession,
options connectionOptions: UIScene.ConnectionOptions
) {
if let activity = connectionOptions.userActivities.first(where: {
$0.activityType == NSUserActivityTypeBrowsingWeb
}) {
Exponea.shared.handleUniversalLink(activity)
}
}If your custom
SceneDelegateimplementsscene(_:continue:)for reasons unrelated to the SDK (for example, Handoff or Shortcuts), it must still explicitly callExponea.shared.handleUniversalLink(_:)(ortrackCampaignClick(url:timestamp:)). The SDK's in-app deeplink handling (UrlOpener) can only detect whether your scene delegate implementsscene(_:continue:)— it can't verify that the implementation forwards to the SDK.If your implementation doesn't forward, this affects SDK-initiated deeplinks only (in-app messages, in-app content blocks). The campaign click isn't tracked, and the link itself silently fails to open for that scene:
UrlOpenertreats the scene as having already handled the link and doesn't fall back to opening the URL.Real, system-delivered universal links (tapped from Notes, Messages, or Safari) aren't affected, since UIKit always dispatches those directly to your real
SceneDelegate.
Legacy AppDelegate lifecycle
If your app doesn't use UIApplicationSceneManifest, handle universal links in AppDelegate:
func application(_ application: UIApplication,
continue userActivity: NSUserActivity,
restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
guard Exponea.shared.handleUniversalLink(userActivity) else { return false }
// process the universal link and return true once it has been processed
return true
}You can also call Exponea.shared.trackCampaignClick(url:timestamp:) directly if you already extracted userActivity.webpageURL.
Universal Link parameters are automatically tracked in session_start events when a new session is started for a given Universal Link click. If the URL contains a parameter xnpe_cmp then an additional campaign event is tracked. The parameter xnpe_cmp represents a campaign identifier typically generated for Email or SMS campaigns.
If an existing session is resumed by opening a universal link, the resumed session is NOT attributed to the universal link click, and the universal link click parameters are not tracked in the
session_startevent. Session behavior is determined by theautomaticSessionTrackingandsessionTimeoutparameters described in Configuration for iOS SDK. Please consider this in case of manual session handling or while testing universal link tracking during the development.
The SDK might not be initialized when
.trackCampaignClick()is called. In this case, the event will be sent to the Marketing backend after the SDK is initialized withExponea.shared.configure().
Updated 19 days ago

