Skip to content
Dashboard

Usage

Detour delivers links to your app through the Detour.shared singleton. Call resolveInitialLink once at launch to read the link that opened the app, or the deferred link recorded before the install. Call processLink for every link that arrives while the app is running.

Both return a DetourResult with the matched route, which you navigate to.

Build the config once and pass the same value to every call:

import Detour
let detourConfig = DetourConfig(
apiKey: "<YOUR_DETOUR_API_KEY>",
appID: "<YOUR_DETOUR_APP_ID>",
shouldUseClipboard: true,
linkProcessingMode: .all
)

Call resolveInitialLink exactly once during cold start:

  • If the app uses UIScene, call the connectionOptions overload from scene(_:willConnectTo:options:).
  • Otherwise, call the launchOptions overload from application(_:didFinishLaunchingWithOptions:).

Do not call both overloads for the same launch. The SDK processes the initial link once per session, so the second call returns an empty result.

For a scene-based app, UIKit supplies cold-start URLs and user activities in UIScene.ConnectionOptions:

SceneDelegate.swift
func scene(
_ scene: UIScene,
willConnectTo session: UISceneSession,
options connectionOptions: UIScene.ConnectionOptions
) {
Detour.shared.resolveInitialLink(
config: detourConfig,
connectionOptions: connectionOptions
) { result in
if let link = result.link {
// Navigate using link.route / link.pathname / link.params
}
}
}
AppDelegate.swift
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
Detour.shared.resolveInitialLink(config: detourConfig, launchOptions: launchOptions) { result in
if let link = result.link {
// Navigate using link.route / link.pathname / link.params
}
}
return true
}

For a complete, runnable integration with an AppDelegate and SwiftUI state, see Examples.

Verified https:// links arrive as an NSUserActivity. Use the callback for your app’s lifecycle:

func scene(_ scene: UIScene, continue userActivity: NSUserActivity) {
guard userActivity.activityType == NSUserActivityTypeBrowsingWeb,
let url = userActivity.webpageURL else {
return
}
Task { @MainActor in
let result = await Detour.shared.processLink(url, config: detourConfig)
// handle result.link
}
}

Registered links such as myapp://product/42 arrive as a URL. Use the callback for your app’s lifecycle:

func scene(
_ scene: UIScene,
openURLContexts URLContexts: Set<UIOpenURLContext>
) {
guard let url = URLContexts.first?.url else { return }
Task { @MainActor in
let result = await Detour.shared.processLink(url, config: detourConfig)
// handle result.link
}
}

processLink and resolveInitialLink both return a DetourResult:

processed
Type Bool

Link processing finished.

link
Type DetourLink?

The matched link, or nil.

DetourResult also forwards the link’s fields, so you can read them without unwrapping:

route
Type String?

Full in-app route, query string included: /details?utm=1.

pathname
Type String?

Path without the query: /details.

params
Type [String: String]

Query params, already parsed.

linkType
Type LinkType?

Where the link came from: .deferred, .verified, or .scheme.

linkURL
Type URL?

The original URL.

DetourConfig.linkProcessingMode controls which link sources the SDK handles:

ValueDeferredUniversalCustom scheme
.all (default)YesYesYes
.webOnlyYesYesNo
.deferredOnlyYesNoNo

Use .deferredOnly when your own routing layer already handles runtime links.

Before it delivers a Universal Link, the SDK checks the click against your plan’s limit. Within the limit, links resolve normally.

The check runs when processLink receives a non-nil config, and on the Universal Link path inside resolveInitialLink.

The link is not delivered: result.link is nil, so there is nothing to navigate to. The SDK logs [Detour:CLICK_LIMIT_ERROR] with clicksInPeriod and effectiveLimit, under the subsystem com.swmansion.detour.

This flow applies only to Universal Links.

  • Deferred and short links — the same plan limits apply on the backend, but the SDK raises no click-limit error. An over-limit link fails to resolve and result.link stays nil.
  • Custom scheme links — resolved on device, never limit-checked.

For per-plan limits, pay-as-you-go overage, and how to raise them, see Billing & Payments.

resolveInitialLink mounts analytics. Mount them yourself only if you process links without it:

Detour.shared.mountAnalytics(config: detourConfig)

Once mounted, the SDK logs one event by itself: app_open, a retention event sent once per session on the first mount. Universal Link opens are not client events. The backend records them through the click endpoint (see Click limits).

Everything else you log yourself:

DetourAnalytics.logEvent(.addToCart, data: ["sku": "abc"])
DetourAnalytics.logEvent("promo_banner_tap", data: ["placement": "home_top"])
DetourAnalytics.logRetention("day_7_return")