Deep Linking in iOS 14

With the release of iOS 14, companies like Branch and Facebook sounded the alarm with respect to IDFA changes. These changes gave users much more control...

Deep Linking in iOS 14

With the release of iOS 14, companies like Branch and Facebook sounded the alarm with respect to IDFA changes. These changes gave users much more control over how, when, and to what extent their activity could be tracked across apps and websites. While Apple has agreed to postpone their major IDFA changes until 2021, another portion of the iOS 14 release has caused some (smaller) waves of its own: deep linking support.

In this blog post, we’ll take a look at changes Apple has made to the deep linking configuration requirements and address common pitfalls along the way.

Deep Linking Primer

The concept of deep linking is a simple one: navigating to a web URL on a mobile device will open that website’s native app instead of a browser, if the user has the associated app installed. This provides users with an easy route into a native app experience, which is often preferred over the mobile web experience.

Behind the scenes, the mobile device’s operating system, the target website, and the native app are all working together to establish a chain of trust: the OS validates the configuration published by the target website, the target website identifies its associated mobile app, and the app reports its associated web domains to the operating system. Provided that each portion of this trust chain is satisfied and properly configured, a user will seamlessly flow from clicking on a link on webpage into your mobile app. Any missing piece of this chain, however, means the user will proceed to the page within their mobile device’s browser.

iOS 13 and Below

mcarter-deep-linking-1.png

On devices running iOS 13 and below, the operating system attempts to validate a deep linking configuration for an app when that app is installed on the device. Assuming your app specifies www.yourdomain.com as an applink in its Entitlements.plist, the device will send a request to https://www.yourdomain.com/.well-known/apple-app-site-association in an attempt to verify that this domain “belongs to” the app. If this configuration file doesn’t properly identify the app, the device assumes that the domain does not belong to the app developer – meaning deep links to yourdomain.com will not be handled by your app. The device will also send a verification request to the apple-app-site-association URL for each of the domains specified in the app’s Entitlements.plist when an update is installed.

iOS 14

mcarter-deep-linking-2.png

In iOS 14, there’s a small but significant change to this process. Verification requests sent to the apple-app-site-association URL for each domain listed in an app’s Entitlements.plist are no longer sent by the device itself. Instead, the device will request this information from Apple, whose CDN will make the request to the domain. The presents some potential hurdles:

  • Apple’s CDN sends requests to domains which can be identified and accidentally blocked as “bot traffic” by web servers, especially those under the protection of products like Akamai or Cloudflare.
  • Apple caches your web server’s apple-app-site-association content, but they have not yet released any details about the duration of this cache. A post I came across suggested it was on the order of hours.
  • Devices with internal apps (or internal Enterprise test releases of apps) that rely on intranet domains for deep linking will need to be configured as MDM devices; it is no longer possible for an unmanaged device connected to a VPN to validate an AASA file directly – it must now go through Apple’s CDN.

Working with Apple’s CDN

Official documentation from Apple regarding its CDN and how/when it makes AASA file requests is still TBD. However, through various rounds of testing we have identified the following general guidelines that will lead to a successful deep linking configuration for your app on iOS 14:

1. If applicable, ensure bot protection and/or WAF products handling traffic before it reaches your webserver(s) allow traffic that identifies itself as Apple-Bot/1.0.0 (not that the version string may change, as again – there’s no official documentation from Apple at the time this blog post was written).

In working with a client recently, we discovered that their security product was identifying this as malicious bot traffic and as a result was periodically blocking the request altogether.

2. The /.well-known/apple-app-site-association file hosted by your webserver(s) should be returned to clients within 2 seconds.

The same client’s security product was also applying a “slow” action response to requests made the Apple CDN bot, which led to response times of around 5-10 seconds. This led to the device getting a failed validation status from Apple when asking to verify the domain.

3. Make sure you host your AASA file at https://www.yourdomain.com/.well-known/apple-app-site-association (and note that Apple will now only request the AASA file over HTTPS).

Hosting the file at https://www.yourdomain.com/apple-app-site-association is officially deprecated in iOS 13.

4. Your webserver should send responses to AASA file requests with the application/json MIME type.
5. If your app targets an internal domain for deep links, the device will need to be managed with an MDM profile, which will require some legwork within your or your client’s organization.

In your app’s Entitlements.plist, you can specify an “alternate mode” for each deep link domain as a query parameter, e.g. “applinks:internaldomain.com?mode=managed”. For more information about this and the “developer” mode, visit https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_developer_associated-domains

6. Apple’s currently published documentation for the AASA file format showcases the newest features.

In order to continue to support iOS 13 and below, ensure you include the “apps” key in the “applinks” dictionary, and continue to use “paths” within each of your apps’ details dictionaries. See below for an example.

Backwards-compatible AASA File Format

To support devices on older versions of iOS, it’s important that your AASA file continues to include certain JSON keys. New JSON keys that give you finer control over your app’s deep linking configuration can be safely mixed in without causing issues.

{
  "applinks": {
    "apps": [],
    "details": [{
      "appID": "ABCXYZ123.com.example.aasa",
      "paths": [
        "/foo/*",
        "NOT /bar/*",
        "NOT /baz/*"
      ],
      "components": [
        {"/": "/foo/*"},
        {
          "/": "/bar/*",
          "exclude": true
        },
        {
          "/": "/baz/*",
          "?": {"fizz": "?*"}
        }
      ]
    }]
  }
}

Note that in this example, we’ve specified the “components” key, which provides more control with respect to deep linking based on query parameters and fragments. On iOS 13+, if “components” is present, the “paths” key is ignored. In our example, an iOS 12 device would not deep link any /baz/* URLs; on iOS 13+ it would trigger a deep link only if the fizz query parameter was present with a value of one of more characters.

Further Reading

For more information about the general setup and requirements to allow deep linking into your iOS app, check the Apple documentation on supporting associated domains: https://developer.apple.com/documentation/safariservices/supporting_associated_domains

If you are still struggling with confirming the AASA file configuration on iOS 14, it may be beneficial to produce a sysdiagnose device log dump immediately after installing your app on an iOS 14 device. In this log archive, a text file named swcutil_show.txt is included that provides feedback on AASA file confirmation attempts made by the device. More information can be found at https://ios13.dev/universal-links-debugging-on-ios-13-cjwsux93w001p6ws1swtstmzc

Bonus note:

If you have trouble performing the steps on a device to trigger a sysdiagnose log dump, you can enable an assistive UI component to help: On the device, go to “Settings > General > Accessibility > AssistiveTouch > Single Tap” and select “Analytics”. Enable AssistiveTouch and click the button to start the process.

The JBS Quick Launch Lab

Free Qualified Assessment

Quantify what it will take to implement your next big idea!

Our assessment session will deliver tangible timelines, costs, high-level requirements, and recommend architectures that will work best. Let JBS prove to you and your team why over 24 years of experience matters.

Get Your Assessment