> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://amer.developers.trustly.com/llms.txt.
> For full documentation content, see https://amer.developers.trustly.com/llms-full.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://amer.developers.trustly.com/_mcp/server.

# Migrate from custom schemes (iOS)

If your application currently uses custom URL schemes such as myapp\://product?id=123, Trustly recommends migrating to Universal Links. Custom schemes are less secure and provide a poorer user experience when compared to Universal Links.

To avoid customer disruptions, implement Universal Links as the primary deep linking method while keeping the custom scheme active as a fallback for older app versions or specific internal routing cases.

### Required changes

The migration process involves shifting your infrastructure from a local, app-specific protocol to a verified, web-based standard.

The following table summarizes the key changes required to move from custom URL schemes to Universal Links.

| Component           | Required Change                                                                                        |
| :------------------ | :----------------------------------------------------------------------------------------------------- |
| Deep Link URL       | Change from a custom protocol (`myapp://path`) to a standard web URL ( `https://yourdomain.com/path`). |
| Web Server          | You must host the `apple-app-site-association` file and serve content over HTTPS.                      |
| Xcode Configuration | You must add the Associated Domains capability to your target.                                         |
| Code Implementation | Change the primary entry point from `openURL` to `continueUserActivity`.                               |

### Code migration strategy

When migrating, the most efficient approach is to create a unified route handler. This allows your app to accept links from both sources (Universal Links and custom URL schemes) and funnel them into a single logic flow. This ensures that users land on the same screen regardless of which link type they click.

### Implement the Universal Link handler

Implement the `scene(_:continue:)` method in your SceneDelegate to intercept the modern HTTPS links. Instead of handling the navigation logic directly, the URL is extracted and passed to a shared helper function. For example:

```swift
func scene(_ scene: UIScene, continue userActivity: NSUserActivity) {
    // Validate that this is a Universal Link
    guard userActivity.activityType == NSUserActivityTypeBrowsingWeb,
          let incomingURL = userActivity.webpageURL else {
        return
    }
    
    // Pass the URL to your unified router
    handleDeepLink(url: incomingURL) 
}
```

### Update the custom URL Scheme handler

Update your existing `scene(_:openURLContexts:)` method to pass the legacy URL to the shared helper function instead of duplicating your routing logic. For example:

```swift
func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
    guard let urlContext = URLContexts.first else {
        return
    }
    
    let incomingURL = urlContext.url
    
    // Check if the scheme matches your legacy custom scheme
    if incomingURL.scheme == "myapp" { 
        print("Handling legacy custom URL scheme: \(incomingURL)")
        
        // Pass the URL to the same unified router
        handleDeepLink(url: incomingURL) 
    }
}
```

### Create the unified router

Create the `handleDeepLink(url:)` function to parse the following URL types:

* Universal Links: The route is usually defined in the path. For example, `https://site.com/product`.
* Custom schemes: The route is often defined in the host. For example, `myapp://product`.

The following example demonstrates how to normalize these differences and execute the navigation:

```swift
func handleDeepLink(url: URL) {
    var route: String?
    var queryItems: [String: String] = [:]

    // 1. Normalize the route based on the scheme
    if url.scheme == "https" {
        // Universal Link: Route is in the path (e.g., /products)
        route = url.path 
    } else if url.scheme == "myapp" {
        // Custom Scheme: Route is often the host (e.g., myapp://products)
        route = url.host 
    } else {
        return // Unknown scheme
    }
    
    // 2. Parse query parameters (e.g., ?id=123)
    if let components = URLComponents(url: url, resolvingAgainstBaseURL: true),
       let items = components.queryItems {
        for item in items {
            queryItems[item.name] = item.value
        }
    }
    
    // 3. Execute Navigation
    // Check if the route contains the target keyword
    if route?.contains("products") == true {
        if let id = queryItems["id"] {
            print("Unified Router: Navigating to Product ID: \(id)")
            // Trigger your navigation controller push here...
        }
    } else if route?.contains("profile") == true {
        print("Unified Router: Navigating to Profile View")
        // Trigger your navigation controller push here...
    }
}
```