Back to blog

Implement Universal Links on iOS, iPadOS, and macOS

Published on by Daniel

Universal Links let you deep-link into your app from anywhere a URL can be tapped: Safari, Messages, Notes, other apps.

If your app is installed, iOS/iPadOS/macOS opens it and passes in the URL to your app. If not, the link loads in Safari.

Step 1. Get Identifiers

First, you need to get the identifiers for your app that will be used in later steps.

  1. Go to Apple Developer > Identifiers. Apple Developer Identifiers list page

  2. Click your app, then grab App ID Prefix and Bundle ID. You’ll use these in your Apple-App-Site-Association file. App Developer Identifier page for an app

Step 2. Create Apple-App-Site-Association (AASA) file

Create a file named apple-app-site-association (no .json extension). Use this structure:

{
  "applinks": {
    "details": [
      {
        "appID": "ABCDE12345.com.example.app",
        "components": [
          {
            "/": "/*",
            "comment": "Matches all urls in the domain."
          }
        ]
      }
    ]
  }
}

Replace ABCDE12345.com.example.app with your <App ID Prefix>.<Bundle ID> from Step 1.

Step 3. Register a Domain

Register a domain and point it to a host that supports:

  • HTTPS (with valid SSL cert)
  • Serving static files without redirects

Step 4. Host the AASA file

Upload the AASA file to: https://example.com/.well-known/apple-app-site-association

It must be:

  • Accessible via HTTPS
  • No redirects
  • No .json extension

Step 5. Configure your app

In Xcode, under Signing & Capabilities:

  • Add Associated Domains
  • Add applinks:example.com, but with the domain you got in Step 3.

Associated Domains configuration in Xcode

Optional: repeat for any additional domains.

If your app uses Scenes

In your SceneDelegate:

/// This is called if your your app needs to launch when a universal link is delivered.
func scene(_ scene: UIScene, willConnectTo
           session: UISceneSession,
           options connectionOptions: UIScene.ConnectionOptions) {    
    // Get URL components from the incoming user activity.
    guard let userActivity = connectionOptions.userActivities.first,
        userActivity.activityType == NSUserActivityTypeBrowsingWeb,
        let incomingURL = userActivity.webpageURL else {
        return
    }
    // Route to the correct screen in your app
    handleUniversalLink(url: incomingURL)
}

/// This is called if your app is in memory when a universal link is delivered.
func scene(
    _ scene: UIScene,
    continue userActivity: NSUserActivity
) {
  guard userActivity.activityType == NSUserActivityTypeBrowsingWeb,
        let incomingURL = userActivity.webpageURL else {
    return false
  }

  // Route to the correct screen in your app
  handleUniversalLink(url: incomingURL)
}

If your app doesn’t use Scenes

In your AppDelegate:

func application(_ application: UIApplication,
  continue userActivity: NSUserActivity,
  restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
  guard userActivity.activityType == NSUserActivityTypeBrowsingWeb,
        let incomingURL = userActivity.webpageURL else {
    return false
  }

  // Route to the correct screen in your app
  handleUniversalLink(url: incomingURL)
  return true
}

If you have a macOS app

In your AppDelegate:

func application(_ application: NSApplication,
                     continue userActivity: NSUserActivity,
                     restorationHandler: @escaping ([NSUserActivityRestoring]) -> Void) -> Bool
{
  // Get URL components from the incoming user activity.
  guard userActivity.activityType == NSUserActivityTypeBrowsingWeb,
    let incomingURL = userActivity.webpageURL else {
    return false
  }
  // Route to the correct screen in your app
  handleUniversalLink(url: incomingURL)
  return true
}
  1. Install your app on a device.
  2. In the Apple Notes app, create some sample links that you want to test, like https://example.com/mylink.
  3. Tap that link and if all went well, it should open in your app!

External Resources