Episode #234

Universal Links

16 minutes
Published on September 2, 2016

This video is only available to subscribers. Get access to this video and 572 others.

Universal Links is a great feature that allows us to render content in an app when a user hits a known URL, instead of opening Safari. For content-based applications, this can be especially useful, as often records that exist on the web also exist in the app. We'll leverage our work from Episode 231 on Shared Web Credentials and extend the demo app to take advantage of Universal Links.

Episode Links

Adding Universal Links Support

First, we need to add the appropriate entitlement. We can do this on the Capabilities tab in the project's settings. We'll define a new Associated Domain, with a value of:

applinks:desolate-springs-88496.herokuapp.com

Next, we need to update our server. We'll modify the response for /apple-app-site-association to include a new applinks section:

get '/apple-app-site-association' do
  content_type 'application/json'
  {
    "webcredentials": {
      "apps": [
        "C2K3864N2N.com.nsscreencast.SharedWebCredentialsDemo"
      ]
    },
    "applinks": {
      "apps": [],
      "details": [
        {
          "appID": "C2K3864N2N.com.nsscreencast.SharedWebCredentialsDemo",
          "paths": ["/episodes", "/episodes/*"]
        }
      ]
    }
  }.to_json
end

Note that the apps key must be an empty array, according to the docs.

Handling Universal Link Taps

Now we need to open our AppDelegate and handle the case when a user taps on a universal link. To do this, we leverage the Handoff API, specifically the application:continueUserActivity:restorationHandler: method:

 func application(application: UIApplication, continueUserActivity userActivity: NSUserActivity, restorationHandler: ([AnyObject]?) -> Void) -> Bool {
        // verify that this is the right activity type, we have a valid URL
        guard userActivity.activityType == NSUserActivityTypeBrowsingWeb,
        let url = userActivity.webpageURL,
        let path = url.path
        else { return false }

        // try to match the path part of the URL to our content.
        // here we're using a statically defined array of episodes as our source.
        // this could be Core Data, or however you have your data stored.
        if let match = SampleEpisodes.filter({ $0.path == path }).first {
            let episodeVC = EpisodeViewController(episode: match)
            let episodesRoot = EpisodesViewController()
            let nav = UINavigationController()
            nav.viewControllers = [episodesRoot, episodeVC]

            window?.rootViewController?.presentViewController(nav, animated: true, completion: nil)
            return true
        }

        // if we can't handle it, default to open the URL in Safari
        application.openURL(url)
        return false
    }

This episode uses Xcode 7.3, Swift 2.3.