iOS Setup

Universal Links

Universal Links is a feature of iOS that allows seamless linking of content that can be accessed through both a browser and within your app. Rover supports Universal Links for presenting Experiences.

An example of a universal Experience URL might look like https://myapp.rover.io/my-great-experience. When a user opens this URL on a mobile device it will present the Experience directly in your app. If the user doesn't have your app installed it will fall-back to viewing the web-based version of the Experience in the browser.


Initialize Rover

Similar to the URL scheme used by Rover's Deep Links setup, each Rover account has a unique domain that is used to construct Rover universal links. You can find the domain assigned to your Rover account in the Rover Settings app scrolling down from the general settings to the associated domain.

Also similar to Deep Links, the Rover SDK needs to know the domain associated with your account. When you initialize Rover, set the associatedDomains property of the UIAssembler to include the domain found in the Settings app.

Rover.initialize(assemblers: [
    FoundationAssembler(),
    DataAssembler(accountToken: "YOUR_SDK_TOKEN"),
    UIAssembler(associatedDomains: ["myapp.rover.io"], urlSchemes: ["rv-myapp"]),
    ExperiencesAssembler(),
    NotificationsAssembler(),
    LocationAssembler(),
    DebugAssembler()
])

Prepare Your App

In order for the domain assigned to your Rover account to be associated with your app, you need to add the Associated Domains feature to your App ID and add your Rover domain to the list of associated domains in the entitlement. This can be done directly from within Xcode. Open your Xcode project or workspace and select your app from the Project Navigator. Then select your app's target and the "Signing & Capabilities" tab. Then press the "+ Capability" button.

Then select "Associated Domains" from the dialog. This will add the capability. Add your Rover domain prefixed with applinks: to the list of associated domains.


Site Association

Before iOS will open URLs with an associated domain, it needs to verify permssion from the domain owner. It does by checking for the presence of a special file named apple-app-site-association uploaded to the domain's web server. Rover will create this file for you and serve it at your associated domain but you need to provide your App ID Prefix and App Store ID which is used in the file's contents.

Note: To learn more about site association read the Support Universal Links guide on Apple's Developer Center.

App ID Prefix

To find your App ID Prefix, sign-in to the Apple Developer Centre and click on "Certificates, Identifiers & Profiles".

Click on "App IDs" in the sidebar, and find your App ID in the list.

Click on your App ID and you will see a value labelled "Prefix".

App Store ID

To find your App Store ID visit the iTunes Link Maker. Select the appropriate country and change the media type to "iOS Apps". Now type the name of your app in the search field and submit.

In the results, click on your app and you will see a "Direct Link" on the bottom of the page. Your App Store ID is the nine-digit number in between id and ?mt.

iOS Settings

Visit the Settings page of the Rover Settings app. Click on the "Link Configuration" heading, then fill in the Team ID and Bundle ID.

Note: If you've already setup APNs, the Bundle Identifier field will be filled in for you. If not, you will need to fill that field in as well.

Save the form and verify the apple-app-site-association file is available in the .well-known directory of your Rover associated domain.

Example: https://myapp.rover.io/.well-known/apple-app-site-association.


When a user taps a link with a domain that is associated with your app, iOS hands your app an NSUserActivity carrying the URL of the link that was tapped. Rover's Router service can handle the userActivity by delegating to the appropriate Rover module. Pass it to the router's handle(_:) method. Where that happens depends on your app's life cycle.

SwiftUI

Handle this in your app scene with the onContinueUserActivity(_:perform:) view modifier, matching on the NSUserActivityTypeBrowsingWeb activity type:

@main
struct MyApp: App {
    var body: some Scene {
        WindowGroup {
            RootView()
                .onContinueUserActivity(NSUserActivityTypeBrowsingWeb) { userActivity in
                    if !Rover.shared.router.handle(userActivity) {
                        // Handle non-Rover universal links here.
                    }
                }
        }
    }
}

UIKit Scene Delegate

If your app has adopted the UIScene life cycle, which is mandatory for apps built with the iOS 27 SDK, iOS delivers the user activity to your UIWindowSceneDelegate and never to the app delegate. Handle both the live callback and the cold-launch connection options:

func scene(_ scene: UIScene, continue userActivity: NSUserActivity) {
    handle(userActivity: userActivity)
}

func scene(
    _ scene: UIScene,
    willConnectTo session: UISceneSession,
    options connectionOptions: UIScene.ConnectionOptions
) {
    // When a universal link launches the app from a terminated state, the activity
    // arrives here instead of in the callback above.
    for userActivity in connectionOptions.userActivities {
        handle(userActivity: userActivity)
    }
}

private func handle(userActivity: NSUserActivity) {
    if Rover.shared.router.handle(userActivity) {
        return
    }
    // Handle non-Rover universal links here.
}

Skipping scene(_:willConnectTo:options:) is an easy mistake to make, because universal links then work in every case except launching the app from cold.

UIKit App Delegate

If your app has not adopted the scene life cycle, iOS calls application(_:continue:restorationHandler:) on your app delegate instead:

func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
    return Rover.shared.router.handle(userActivity)
}

This method is not called in a scene-based app

The moment your app gains a scene delegate, UIKit stops calling application(_:continue:restorationHandler:) and gives you no warning that it has. Rover universal links then silently stop opening. Use the scene delegate version above instead.

Testing Universal Links

Universal Links are notoriously difficult to test. iOS will only verify the associated domain when a "production" release of your app is installed. Until that happens your Rover domain will not be associated with your app and Universal Links will not function. TestFlight does count as a "production" release and deploying a build through TestFlight may be a viable workaround.

Rover Routing and Threads

Only call Rover's router.handle() method from the main thread. Be aware that certain routing tools, such as Firebase, may call your handlers on background threads, so be sure to switch back to the main thread before calling Rover.

Previous
Deep Links