# API Reference

## Detour

Main SDK singleton, accessed via `Detour.shared`. All methods are `@MainActor`.

### Link processing methods

#### `processLink(_:)`

Synchronous. Converts a URL into a `DetourResult`.

```swift
func processLink(_ url: URL) -> DetourResult
```

The result carries the parsed route, pathname, params, and link type. Short links are not resolved. Infrastructure URLs, for example Expo dev client URLs, return an empty result.

#### `processLink(_:config:)` with a `URL`

Async. Processes a URL you receive at runtime, and resolves short links when you pass `config`.

```swift
func processLink(
    _ url: URL,
    config: DetourConfig?
) async -> DetourResult
```

Call it from `onOpenURL` in SwiftUI or `application(_:open:)` in UIKit.

For a Universal Link with a non-`nil` `config`, the SDK first checks the click against your plan's click limit. Over the limit, the link is not delivered and `result.link` is `nil`. See [Click limits](https://detour.swmansion.com/docs/sdk/ios/sdk-usage#click-limits).

#### `processLink(_:config:)` with a `String`

Async. The same as the `URL` variant, for a raw link string.

```swift
func processLink(
    _ rawLink: String,
    config: DetourConfig? = nil
) async -> DetourResult
```

Accepts full URLs and plain paths. With `config`, a single-segment web path, which is a short link, is resolved to its full destination URL.

#### `resolveInitialLink(config:launchOptions:completion:)`

UIKit. Resolves the link that launched the app. Call it from `application(_:didFinishLaunchingWithOptions:)`.

```swift
func resolveInitialLink(
    config: DetourConfig,
    launchOptions: [UIApplication.LaunchOptionsKey: Any]?,
    completion: @escaping @Sendable (DetourResult) -> Void
)
```

It checks, in order:

1. **Launch URL** from `launchOptions`
2. **Universal Link** from user activities in `launchOptions`
3. **Deferred link** from the Detour API (first install only)

It runs once per app session, and it mounts analytics.

#### `resolveInitialLink(config:connectionOptions:completion:)`

SceneDelegate and SwiftUI. The same as the UIKit variant, for scene-based apps.

```swift
func resolveInitialLink(
    config: DetourConfig,
    connectionOptions: UIScene.ConnectionOptions,
    completion: @escaping @Sendable (DetourResult) -> Void
)
```

It takes `UIScene.ConnectionOptions`. Call it from `scene(_:willConnectTo:options:)` or the SwiftUI app lifecycle.

### Analytics methods

#### `mountAnalytics(config:)`

Mounts the analytics system.

```swift
func mountAnalytics(config: DetourConfig)
```

`resolveInitialLink` calls it for you. Call it yourself only if you use `processLink` without `resolveInitialLink`.

#### `unmountAnalytics()`

Unmounts the analytics system and stops listening for events.

```swift
func unmountAnalytics()
```

### Utility methods

#### `resetSession(allowDeferredRetry:)`

Resets the session state, so `resolveInitialLink` can run again.

```swift
func resetSession(allowDeferredRetry: Bool = false)
```

With `allowDeferredRetry: true` it also resets the first-launch flag. Use it to test the deferred link flow locally.

## DetourConfig

SDK configuration. All properties are immutable and the struct conforms to `Sendable`.

```swift
public struct DetourConfig: Sendable {
    public let apiKey: String
    public let appID: String
    public let shouldUseClipboard: Bool
    public let linkProcessingMode: LinkProcessingMode
}
```

### Initializer

```swift
DetourConfig(
    apiKey: String,
    appID: String,
    shouldUseClipboard: Bool = true,
    linkProcessingMode: LinkProcessingMode = .all
)
```

<PropertyList>
  <Property name="apiKey" type={"String"} required>
    Publishable API key from the Detour Dashboard. Sent as a Bearer token on every API call: deferred link matching, short-link resolution, and analytics.
  </Property>
  <Property name="appID" type={"String"} required>
    Unique app identifier from the Detour Dashboard. Routes API requests to the correct app.
  </Property>
  <Property name="shouldUseClipboard" type={"Bool"} default={"true"}>
    When `true`, the SDK reads the device clipboard and includes it in the probabilistic fingerprint for deferred link matching. May trigger the iOS system paste permission prompt. Set it to `false` to skip clipboard access.
  </Property>
  <Property
    name="linkProcessingMode"
    type={"LinkProcessingMode"}
    typeHref="#linkprocessingmode"
    default={".all"}
  >
    Which link sources the SDK processes. See [LinkProcessingMode](#linkprocessingmode) for each value.
  </Property>
</PropertyList>

## DetourResult

Result returned by all link processing methods. Contains a processing flag and an optional parsed link.

```swift
public struct DetourResult: Sendable {
    public let processed: Bool
    public let link: DetourLink?
}
```

<PropertyList>
  <Property name="processed" type={"Bool"}>
    `true` when the SDK has finished processing the link, regardless of whether a link was found.
  </Property>
  <Property name="link" type={"DetourLink?"} typeHref="#detourlink">
    Parsed link data, or `nil` if no link was matched.
  </Property>
</PropertyList>

### Convenience computed properties

<PropertyList>
  <Property name="route" type={"String?"}>
    Shorthand for `link?.route`, the full in-app route including query string.
  </Property>
  <Property name="pathname" type={"String?"}>
    Shorthand for `link?.pathname`, the path without query string.
  </Property>
  <Property name="params" type={"[String: String]"}>
    Shorthand for `link?.params ?? [:]`, the parsed query parameters.
  </Property>
  <Property name="linkType" type={"LinkType?"} typeHref="#linktype">
    Shorthand for `link?.type`, which says how the link arrived.
  </Property>
  <Property name="linkURL" type={"URL?"}>
    `link?.url` parsed into a `URL`. `nil` when there is no link or the string is not a valid URL.
  </Property>
</PropertyList>

### Factory method

```swift
static func empty() -> DetourResult
```

Returns a result with `processed: true` and `link: nil`. Used internally when no link is matched.

## DetourLink

Parsed link data returned inside a `DetourResult`.

```swift
public struct DetourLink: Sendable {
    public let url: String
    public let route: String
    public let pathname: String
    public let params: [String: String]
    public let type: LinkType
}
```

<PropertyList>
  <Property name="url" type={"String"}>
    Full URL string that was matched or opened.
  </Property>
  <Property name="route" type={"String"}>
    In-app route for navigation, as a path with query string, first path segment (app hash) stripped (e.g. `/promo/123?utm=1`).
  </Property>
  <Property name="pathname" type={"String"}>
    Route path without query string (e.g. `/promo/123`).
  </Property>
  <Property name="params" type={"[String: String]"}>
    Parsed query parameters as key-value pairs.
  </Property>
  <Property name="type" type={"LinkType"} typeHref="#linktype">
    How the link arrived in the app.
  </Property>
</PropertyList>

## LinkProcessingMode

```swift
public enum LinkProcessingMode: String, Sendable {
    case all
    case webOnly = "web-only"
    case deferredOnly = "deferred-only"
}
```

Controls which link sources are processed:

| Value | Behavior |
| --- | --- |
| `.all` (default) | Processes all link sources: deferred links from the Detour API, Universal Links (verified HTTP/HTTPS URLs), and custom scheme deep links |
| `.webOnly` | Same as `.all` but filters out custom scheme links. Only deferred links and Universal Links (HTTP/HTTPS) are processed |
| `.deferredOnly` | Only processes deferred links from the Detour API. Skips Universal Link and scheme link detection in `resolveInitialLink`. Recommended when your app handles runtime links separately |

## LinkType

```swift
public enum LinkType: String, Codable, Sendable {
    case deferred
    case verified
    case scheme
}
```

Describes how the link arrived in the app:

| Value | Meaning |
| --- | --- |
| `.deferred` | The link was obtained from the Detour deferred-link API. This happens when a user who does not have the app installed clicks a Detour link, installs the app, and opens it. The SDK retrieves the original link from the backend on first launch |
| `.verified` | The link arrived as a Universal Link, an HTTP/HTTPS URL that iOS verified belongs to your app via the apple-app-site-association file and delivered directly to it |
| `.scheme` | The link arrived via a custom URL scheme (e.g. `myapp://path`). These are non-HTTP URLs that iOS routes to your app based on the registered scheme |

## DetourAnalytics

Analytics module for logging events to the Detour backend. Accessed via static methods on the `DetourAnalytics` class. Analytics are automatically mounted when calling `resolveInitialLink`. Mount manually only if you use `processLink` without `resolveInitialLink`.

The SDK automatically logs an `app_open` retention event when analytics are first mounted in a session. Universal Link opens are recorded server-side through a dedicated click endpoint (see [Click limits](https://detour.swmansion.com/docs/sdk/ios/sdk-usage#click-limits)) rather than as a client event.

All API requests the SDK sends also include an `X-SDK: ios/<version>` header for diagnostics and SDK-usage analysis.

### Methods

#### `mount(config:)`

Mounts the analytics system and subscribes to events. A second call does nothing.

```swift
static func mount(config: DetourConfig)
```

#### `unmount()`

Unmounts the analytics system and stops listening for events.

```swift
static func unmount()
```

#### `logEvent(_:data:)` with a `DetourEventName`

Records a user action under a predefined [event name](#detoureventname).

```swift
static func logEvent(
    _ eventName: DetourEventName,
    data: [String: Any]? = nil
)
```

`data` takes a dictionary of any metadata.

#### `logEvent(_:data:)` with a `String`

Records a user action under a custom event name.

```swift
static func logEvent(
    _ eventName: String,
    data: [String: Any]? = nil
)
```

#### `logRetention(_:)`

Records a retention event.

```swift
static func logRetention(_ eventName: String)
```

## DetourEventName

| Category | Event Name | Raw value |
| :--- | :--- | :--- |
| **General** | `.login` | `login` |
|  | `.search` | `search` |
|  | `.share` | `share` |
|  | `.signUp` | `sign_up` |
|  | `.tutorialBegin` | `tutorial_begin` |
|  | `.tutorialComplete` | `tutorial_complete` |
|  | `.reEngage` | `re_engage` |
|  | `.invite` | `invite` |
|  | `.openedFromPushNotification` | `opened_from_push_notification` |
| **Sales** | `.addPaymentInfo` | `add_payment_info` |
|  | `.addShippingInfo` | `add_shipping_info` |
|  | `.addToCart` | `add_to_cart` |
|  | `.removeFromCart` | `remove_from_cart` |
|  | `.refund` | `refund` |
|  | `.viewItem` | `view_item` |
|  | `.beginCheckout` | `begin_checkout` |
|  | `.purchase` | `purchase` |
|  | `.adImpression` | `ad_impression` |

## Related pages

- [Usage](https://detour.swmansion.com/docs/sdk/ios/sdk-usage)
- [Examples](https://detour.swmansion.com/docs/sdk/ios/examples)
- [Testing & Troubleshooting](https://detour.swmansion.com/docs/sdk/ios/testing)