Universal links in iOS

Last day I was asked to integrate deep linking in one of the app that I made last year. It was in objective-C. I had to face so many issues doing this as there are no proper documentation and the one that is available over the internet are not straight forward. Here, I will share what I understood and how I did it.

Disclaimer: To make this article I had to read a lot of stuffs over the internet. Also, I had copied easy to understand examples and sentences from other article to make this article meaningful. 🤓 🤓 🤓

Apple docs link for universal links.

What Is A Deep Link?

If you want to share a pair of shoes from the amazon.com with a friend, you can send a deep link that brings your friend directly to those shoes in the app. Without a deep link, your friend would have to find the amazon app on the App Store or Play Store, open the app to the homepage, locate the Search function, and then try to find the same pair of shoes you did.

Custom URI schemes were the original form of deep linking for mobile apps. They are like creating a “private internet” for your app, with links that look like myapp://path/to/content. The advantage of custom URI schemes is they are easy to set up and most apps already have one. The disadvantage is a user’s device only knows about this “private internet” if the corresponding app is already installed, and there is no graceful fallback option by default.

The workaround approach to deep linking with URI schemes involves using a traditional http:// link to launch a web browser. This link contains a JavaScript redirect to a custom URI scheme, which is executed by the web browser to launch the app. If the redirect attempt fails because the app is not installed, the JavaScript then takes the user to the App Store or Play Store.

This is still the primary approach to deep linking on Android, but Apple began blocking this approach on iOS in 2015 with the release of Universal Links.

What is Apple iOS Universal Links?

How Do Universal Links Work in iOS ?

Before Universal Links, the primary mechanism to open up an app when it was installed was by trying to redirect to an app’s URI scheme (registered in the app’s PLIST like so) in Safari. This put the routing logic in Safari, but there was no way to check if the app was installed or not. This meant that developers would try to call the URI scheme 100% of the time, in the off chance that the app was installed, then fallback gracefully to the App Store when not by using a timer.

iOS 9 Universal Links were intended to fix this. Instead of opening up Safari first when a link is clicked, iOS will check if a Universal Link has been registered (an AASA (apple-app-site-association) file should be there in the domain which contains the bundle id of the app and the paths the app should open) for the domain associated with the link, then check if the corresponding app is installed. If the app is currently installed, it will be opened. If it’s not, Safari will open and the http(s) link will load.

Functionally, it allows you have a single link that will either open your app or open your mobile site.

How to Set Up Universal Links in iOS?

1. Configure your app to register approved domains

  • Enable ‘Associated Domains’ on your app identifier.
Image for post
Image for post
  • Enable ‘Associated Domain’ on in your Xcode project
Image for post
Image for post

The error “Add the associated Domains feature to your App ID” will go away once you enable the Associated Domains in your APP ID in developer.apple.com as in the previous step. If it doesn’t go away, quit and relaunch the xcode few times and it will work 😖😖😖.

  • Add the proper domain entitlement and make sure the entitlements file is included at build: Xcode will do it automatically by itself.
Image for post
Image for post

2. Configure your website to host the ‘apple-app-site-association’ file

What Is An AASA (apple-app-site-association) File?

Let’s look at some basics of the apple-app-site-association file that will help you in building and hosting one on your domain.

The AASA file contains a JSON object with a list of apps and the URL paths on the domain that should be included or excluded as Universal Links. Here is a sample AASA file:

  • appID: Built by combining your app’s Team ID (goto https://developer.apple.com/account/#/membership/ to get the teamID) and the Bundle Identifier. In the example above, JHGFJHHYX is the Team ID and com.facebook.ios is the Bundle ID.
  • paths: Array of strings that specify which paths are included or excluded from association. You can use NOT (before the path — as in the example JSON above) to disable paths. In this case, all the links on this path will go to the web instead of opening the app. You can use * as a wildcard to enable all paths in a directory (apple doc says: Use * to specify your entire website) and ? to match a single character (/archives/201?/ example in the sample JSON). Please note that these strings are case sensitive and that query strings and fragment identifiers are ignored.

Hosting the AASA File on Your Domain

Once you are ready with your AASA file, you can now host it on your domain either at https://<<yourdomain>>/apple-app-site-association or at https://<<yourdomain>>/.well-known/apple-app-site-association.

Upload the apple-app-site-association file to your HTTPS web server. You can place the file at the root of your server or in the .well-known subdirectory.

Important: iOS will only attempt to fetch the AASA file over a secure connection (HTTPS).

So, while hosting the AASA file, please ensure that the AASA file:

  • Is served over HTTPS.
  • Uses application/json MIME type.
  • Don’t append .json to the apple-app-site-association filename.
  • Has a size not exceeding 128 Kb (requirement in iOS 9.3.1 onwards).

Supporting Multiple Apps on the Same Domain

If two or more apps associate with the same content path on the website then the order of the appID, paths dictionary in the details array will determine which app will get precedence.

APPLE APP SITE ASSOCIATION (AASA) VALIDATOR

Link: https://branch.io/resources/aasa-validator/#resultsbox

Next, you need to handle universal links in your app.

Preparing Your App to Handle Universal Links

To support universal links in your app, take the following steps:

  • Add an entitlement that specifies the domains your app supports.
  • Update your app delegate to respond appropriately when it receives the NSUserActivity object.

As mentioned above, in Xcode, open the Associated Domains section in the Capabilities tab and add an entry for each domain that your app supports, prefixed with applinks: .

example: applinks:www.mywebsite.com

Apple doc says to limit this list to no more than about 20 to 30 domains.

To match all subdomains of an associated domain, you can specify a wildcard by prefixing *. before the beginning of a specific domain (the period is required). Domain matching is based on the longest substring in the applinks entries. For example, if you specify the entries applinks:*.mywebsite.com and applinks:*.users.mywebsite.com, matching for the domain emily.users.mywebsite.com is performed against the longer *.users.mywebsite.com entry. Note that an entry for *.mywebsite.com does not match mywebsite.com because of the period after the asterisk. To enable matching for both *.mywebsite.com and mywebsite.com, you need to provide a separate applinks entry for each.

After you do all these steps perfectly, when you click a universal link, the app will open up and the method application:continueUserActivity:restorationHandler will get called in Appdelegate.

When iOS launches your app after a user taps a universal link, you receive an NSUserActivity object with an activityType value of NSUserActivityTypeBrowsingWeb. The activity object’s webpageURL property contains the URL that the user is accessing. The webpage URL property always contains an HTTP or HTTPS URL, and you can use NSURLComponents APIs to manipulate the components of the URL.

func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([Any]?) -> Void) -> Bool {
print("Continue User Activity called: ")
if userActivity.activityType == NSUserActivityTypeBrowsingWeb {
let url = userActivity.webpageURL!
print(url.absoluteString)
//handle url and open whatever page you want to open.
}
return true
}
Image for post
Image for post
Manipulating the components of the URL

For getting the url parameters, use the following function:

//playground code..var str = “https://google.com/contents/someotherpath?category=series&contentid=1562167825"let url = URL(string: str)func queryParameters(from url: URL) -> [String: String] {let urlComponents = URLComponents(url: url, resolvingAgainstBaseURL: false)var queryParams = [String: String]()for queryItem: URLQueryItem in (urlComponents?.queryItems)! {if queryItem.value == nil {continue}queryParams[queryItem.name] = queryItem.value}return queryParams}
// print the url parameters dictionary
print(queryParameters(from: url!))

It will print:

[“category”: “series”, “contentid”: “1562167825”]

Also if you want to check if the app had opened by clicking a universal link or not in the didFinishLaunchingWithOptions method:

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey : Any]? = nil) -> Bool {
var isUniversalLinkClick: Bool = false
if launchOptions[UIApplicationLaunchOptionsUserActivityDictionaryKey] {
let activityDictionary = launchOptions[UIApplicationLaunchOptionsUserActivityDictionaryKey] as? [AnyHashable: Any] ?? [AnyHashable: Any]()
let activity = activityDictionary[“UIApplicationLaunchOptionsUserActivityKey”] as? NSUserActivity ?? NSUserActivity()
if activity != nil {
isUniversalLinkClick = true
}
}
if isUniversalLinkClick {
// app opened via clicking a universal link.
} else {
// set the initial viewcontroller
}
return true
}

Keep in mind:

  • If you instantiate a SFSafariViewController, WKWebView, or UIWebView object to handle a universal link, iOS opens your website in Safari instead of opening your app. However, if the user taps a universal link from within an embedded SFSafariViewController, WKWebView, or UIWebView object, iOS opens your app.
  • It’s important to understand that if your app uses openURL: to open a universal link to your website, the link does not open in your app. In this scenario, iOS recognizes that the call originates from your app and therefore should not be handled as a universal link by your app.

Testing:

Here is a youtube video about Universal links:

Issues:

  • Pasting a Universal Link directly into the Safari URL field doesn’t cause the app to open automatically. If you do this, you will have to manually pull the website down so that a prompt will appear at the top asking you to open the respective app.
  • But, if you paste links in Facebook(app), Twitter(app), Mail(app) or even if you go to Facebook on Safari and then click on a universal link, the app opens directly.
  • Universal links will not works for all the apps in iOS. If you click on a universal link from any of the “BLACK LISTED” apps, it will not open the app. Go to this link to know more.
  • As in step one, for the first time you will have to manually pull down the website and click “open” to open the link using the respective app. The iOS will “remember” to open the app instead of opening the safari if the universal link with the registered domains are clicked.

Here is a good article that you can go through to understand the issues with universal links: Link

Apple Reference :

  1. Supporting_associated_domains_in_your_app
  2. Allowing_apps_and_websites_to_link_to_your_content

Local Machine Hosting https://geek-is-stupid.github.io/2015-04-07-how-to-press-a-link-to-open-an-ios-application-by-using-swift-and-universal-links-on-ios9/

Enjoy!!

If you enjoyed reading this post, please share and give some clapps so others can find it 👏👏👏👏👏 !!!!

If you have any comment, question, or recommendation, feel free to post them in the comment section below!

Written by

iOS and tvOS developer, dreamer, photographer 🤨

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store