#91: Universal Links πŸŒŽπŸ”—


Universal Links arrived with iOS 9. Conceptually they're a way for us to logically tie the content in our app to the content on our website. Once this is done, iOS will be able to launch our app when a user taps a link somewhere, rather than opening our site in Safari. Let's take a further look by adding Universal Links to a fictional Little Bites of Cocoa app. We'll start by registering and setting up SSL for our domain:

Then, we'll head into Xcode and to the Capabilities tab of our project. We'll flip on the switch for Associated Domains, then click the + button and add our domain. Note that we prefix it with the phrase applinks:.

Now, our app will silently make an HTTP GET request to

It will expect us to return some JSON which describes our app's Bundle ID, and which paths should open it.

We'll open all paths on our domain using a wildcard character here, but we could easily, for example, limit to just Bite URLs.

  "applinks": {
    "apps": [],
    "details": {
      "": {
        "paths": [ "*" ]

After creating the file, we'll need to sign it so it is returned with a Content-Type of application/pkcs7-mime on our server. We'll use a command like the one shown here to sign the file. (This part stinks, but there's no way around it).

cat aasa.json | openssl smime -sign
                              -certfile intermediate.pem
                              -outform DER > apple-app-site-association

Lastly, we'll wire up the other side of the equation. When a user opens a Universal Link that iOS recognizes, it will call the same delegate function that's used to implement features like Handoff (Bite #29) and Spotlight Search (Bite #23). We'll check if it's a Universal Links activity type, then use JLRoutes (Bite #62) to open a section of our app.

extension AppDelegate {
  func application(application: UIApplication, continueUserActivity userActivity: NSUserActivity, restorationHandler: ([AnyObject]?) -> Void) -> Bool {

    if userActivity.activityType == NSUserActivityTypeBrowsingWeb {
      return JLRoutes.routeURL(userActivity.webpageURL!)

    return true