How we use Universal Links to provide a seamless login experience in our iOS applications

Stephen Brown
Reach Product Development
7 min readJun 26, 2020

Universal Links enable your users to intelligently follow links to content inside your app or to your website.

I recently had the fantastic opportunity to attend a WWDC Lab to ask some questions about Universal Links and find out how to alleviate some of the issues we were facing. The solutions are detailed throughout this post.

What are Universal Links?

Universal Links have been available since iOS9 and provide a mechanism for your users to open content in your iOS application when tapping on web links. This is fantastic for driving traffic into your iOS application and providing a great native experience for your supported content types, such as news articles and shop listings.

At Reach we have offered this functionality on our article pages for years, meaning that when a user taps an article link inside of Safari, their email app or other supported apps, we attempt to open the article inside the appropriate iOS application.

You can open links from Google searches directly inside your application

Another aspect of Universal Links is they enable us to provide more seamless experiences for flows within the application, such as email verification.

We have recently implemented an email verification flow for users that register accounts using email address and password. The user receives an email with a link they must tap to verify their account.

We could handle this by sending an app link utilising the app scheme such as com.example.app://verify-email. However, this will not work for a subset of users who tap this link on a different device.

Instead, we opted for Universal Links, such as https://www.example.co.uk/verify-email, because this enables us to provide a fallback for those users. They will be directed to the website and allowed to complete their registration there, logging into the application manually once their verification is complete.

The majority of our users get a seamless experience where they tap the link in their email and are directed into our application, their email address is verified by the supplied tokens, and they are logged in automatically.

Universal Links triggering In-App Email Verification

Below I will outline the simple steps required to set up Universal Links, and some of the gotchas that you may run across.

Setting up Universal Links

Setting up for Universal Links is straightforward and takes three steps.

  1. Add the Associated Domains Entitlement to your application
  2. Host an Apple App Site Association (AASA) file on your website
  3. Set up your App Delegate to handle activity

Add the Associated Domains Entitlement to your application

Open Xcode and navigate to the Signing & Capabilities tab of your project. Once there, tap Add Capability and select the Associated Domains option. This will add a section to this window, allowing you to add your domains.

Apple suggests providing both your www and root domains e.g. www.mirror.co.uk and mirror.co.uk.

Associated Domains Entry
The Associated Domains entry in the Xcode Signing & Capabilities window

Host an Apple App Site Association file on your website

The Apple App Site Association file is used to instruct your device which web links on your domain it should attempt to open inside your application.

As of iOS14, you must host this file at:

{domain}/.well-known/apple-app-site-association

Previously, the /.well-known/ path segment was optional, but the choice has now been deprecated.

It must also be available on https://.

You can only have a single version of this file, but there is different functionality offered for pre iOS13 and iOS13+. Luckily, the field names are different and these versions can coexist in the same file, enabling you to provide the improved functionality to your users who have updated their OS.

Pre iOS13

The contents of this file, pre iOS13:

{
"applinks": {
"details": [
{
"appID": "{TEAM_ID}.{BUNDLE_ID}",
"paths": [
"*"
]
}
]
}
}

For each of your app Bundle IDs, you need a separate JSON object stating the appID and the paths that should be opened.

The appID is your App Store Connect Team ID and your app’s Bundle ID separated by a full stop.

The paths are an array of URL paths that you want to handle for your app. You can have multiple paths, so you can be quite specific:

"/tv/tv-news/article-about-tv"

The above will open a specific path.

"/tv/tv-news/*"

The above uses the wildcard operator to instruct your application to open any URL whose path starts with “/tv/tv-news/”

"*"

You can even use the wildcard operator to instruct your application to open every URL on your website.

iOS13+

iOS13 brought changes to the format of the AASA file.

{
"applinks": {
"apps": [],
"details": [
{
"appIDs": ["{TEAM_ID}.{BUNDLE_ID}"],
"components": [
{
"/": "/tv/tv-news/*",
"comment": "Matches any URL whose path starts with /tv/tv-news/"
},
{
"/": "/celebrity/celebrity-news/*",
"exclude": true,
"comment": "Excludes any URL whose path starts with /celebrity/celebrity-news/"
}
]
}
]
}
}

Firstly appID has been replaced by appIDs, allowing you to supply an array of application IDs. This helps if you have multiple apps that follow the same URL matching rules.

Secondly the paths components has been replaced by a components array, allowing for more explicit and ultimately more readable settings. Now for each path that you want to handle, you supply a JSON object with an optional comment, with the ability to set an exclude flag, to tell your application not to open URLs that match the supplied pattern.

More information on the available patterns can be found here.

Set up the App Delegate

Inside your App Delegate you need to handle the incoming URLs your application has been instructed to open. This is handled, in Swift, by the following function:

func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool

You are supplied a NSUserActivity object, which has two properties we want to look at.

webpageURL: The URL of the webpage to be opened

activityType: The type of activity that triggered this function call

Inside this function, you should check that the activityType is set to NSUserActivityTypeBrowsingWeb, and that the webpageURL is definitely one that you want to handle.

Once you have verified this, you can open the appropriate views inside of your application.

However, occasionally some URLs slip through the cracks that you do not want to open. In which case it is suggested that you should utilise UIApplication.open() to open the webpageURL or to open it within a SafariViewController.

How it works

Once you understand each of the components, the mystery around Universal Links quickly disappears.

When your application is installed on a device via Xcode build, TestFlight or from the App Store, iOS will attempt to download the AASA file from your website and store it on the device.

Once downloaded, any links that are tapped that match the patterns in your AASA file will be provided to your app to handle.

It is possible your file will not be downloaded straight away if the device’s battery is low or there is a poor Internet connection. It will be retried periodically and any updates to your AASA file will be downloaded periodically in the same manner.

For this reason it is important to handle any website URLs on your website, for example if you are handling authentication URLs such as Forgotten Password links or Email Verification links.

Once an AASA file is downloaded, it will be associated with your application until it is deleted from the device.

Debugging

If your universal links are not opening in your application, you can check the status of your downloaded AASA file by utilising Sysdiagnose. Using this tool you can check the logs of your application to verify the AASA file has been downloaded, and has been read correctly (e.g. no formatting issues).

Validation of your AASA file could fail if the JSON is invalid, it doesn’t contain the application identifier, or the server provides with a non HTTP 200 OK response.

Your web URLs may also be ignored by iOS if your user long-presses a link and selects to open the URL with Safari via the menu shown below.

URL tap menu
Opening with Safari will block future URLs from being opened until the user uses this menu in the future to select “Open in App”

If you are updating your AASA file during development, deleting the application from your device between updates will enable you to see your changes immediately.

One final note, as of iOS14, Apple will host your AASA files on their own CDN, which should improve downloading of the file, but could see some issues arise with TTLs.

Conclusion

Universal Links are a great tool to provide improved native experiences for accessing your content and more seamless experiences for interactive functionality such as authentication flows. You need to ensure you can handle the functionality on your website (1–3% of requests will bypass Universal Links for assorted reasons), but the resulting functionality should improve your user’s experience with your application and brand.

--

--

Stephen Brown
Reach Product Development

iOS Developer at Reach PLC, building apps for @DailyMirror and other titles. Follow me on Twitter: @IAmStephenBrown