Universal Links: Make the Connection

Learn how to connect your iOS app with your website using Universal Links, so users can tap a link and go directly to the corresponding content in your app!

Version

  • Swift 4.2, iOS 12, Xcode 10
Update note: Owen Brown updated this tutorial to Swift 4.2. He also wrote the original.

Do you have a web site that shares content with an iOS app? Since iOS 9, you can connect them using universal links, meaning that users can now touch an HTTP link on an iPhone and be sent directly to your app!

In this tutorial, you’ll learn how to link an iOS app to a Heroku-hosted web site. Both the app and web site provide reference material for single board computers, so hardware-hobbyist freaks, rejoice! You’re in for a treat. :]

This tutorial assumes you have a basic understanding of iOS and Swift development. If you’re new to these, go through some of our other iOS and Swift tutorials first. Previous experience with Heroku and web development would be beneficial, but it isn’t strictly required.

Lastly, you must have a paid Apple developer account. A free account won’t work, unfortunately.

Note: Universal links are, in many ways, a replacement for apps that register their own URL schemes. They work by providing a small file (served over HTTPS) from the root of a web domain that points to a specific app ID, and then registering that specific app ID with Apple as handling links from that domain.

Because of these requirements, you won’t be able to fully try out universal links without having a real web site accessible via HTTPS and a real app on the App Store, but you can still gain experience with universal links by going through the process of setting everything up.

Getting Started

Start by downloading the materials you need using the Download Materials button at the top or bottom of this tutorial. Open the starter project, then build and run. You’ll see the app’s main view controller, ComputersController.

Universal Links - Main View

Main View

The app shows a few of the most popular single board computers on the market. Touch one of the rows to segue to the details view controller, ComputerDetailController. Here, you can see product-specific information, including the web site, vendor and pricing.

Universal Links - Navigating

Navigating the Views

I’ve also created a basic web site with similar information. Check out the starter web site here.

Single Board Computer's web site

Single Board Computers web site

Don’t fret the Heroku part: It’s just a quick way for you to access the demo web site for the tutorial. Though not required by this tutorial, you may optionally deploy the web site to your own Heroku account. If you want to do so, follow the instructions here.

Now that you’ve seen both the app and the web site, it’s time to dive into the code to connect them. The end goal: Whenever a user taps any of your web site links on an iOS device — such as https://rw-universal-links-starter.herokuapp.com/arduino.html — it will open in the app instead of Safari. As a final bit of polish, the app will even navigate directly to the specific computer detail controller.

To accomplish this, you need to do three things:

  1. Inform the app of the web site.
  2. Inform the web site of the app.
  3. Handle navigation within the app whenever the user taps a link.

There are a couple requirements for creating universal links. First, you must have “write access” to the linked web site, which must use HTTPS, and you must be able to upload a file to it. Second, you must own the associated app so that you can edit its provisioning profile “capabilities” to register the web site and be able to deploy it to the App Store.

For these reasons, you won’t be able to test the tutorial app. However, you’ll still go through the process to learn how it’s done.

Configuring the App

You first need to configure the app to handle universal links. Open the UniversalLinks.xcodeproj from the Starter folder and do the following:

  1. Select the UniversalLinks project.
  2. Select the UniversalLinks target.
  3. Select the Capabilities tab.
  4. Scroll to and turn ON Associated Domains.
Turn On Capabilities

Turn On Capabilities

You may get a prompt to Select a Development Team to Use for Provisioning. Select any paid Apple developer team that you’re on and press Select to continue.

If you see a prompt to Add an Apple ID Account, it means that you’re not currently signed in to any accounts. In this case, press the Add button and sign into a paid Apple developer team of which you’re a member.

It’s important that this be a paid account. If you use a free account, Xcode may consistently crash; this appears to be an Xcode bug.

You may also see an error message, in the Signing section of the general tab, saying “The app ID ‘com.razeware.UniversalLinks’ cannot be registered to your development team. Change your bundle identifier to a unique string to try again.” This is because the app identifier is already taken by the original tutorial app.

In your own app, make sure your app identifier is unique (following reverse domain name notation). For this tutorial app, however, it’s OK to simply ignore this message.

Next, press + and add the following domain: applinks:rw-universal-links-final.herokuapp.com. If you followed the instructions to launch your own Heroku app, then replace the string after the colon with the full domain name of your Heroku app.

Be sure to add the applinks prefix. You should now see the following:

Associated Domains

Associated Domains

The first step is complete! The app is now aware of the web site’s URL. Next, you’ll configure the web site.

Configuring the Web site

Now, you need to let the web site in on the “big secret.” To do this, create an apple-app-site-association file. This file isn’t new to the iOS world: It’s the same file used in iOS 8 for web credentials and Handoff. The format matches that of a JSON file, but it CANNOT have the .json extension. Don’t use any filename extension and, to preserve your debugging sanity, double-check that the file name matches exactly!

universal links

Create a new file called apple-app-site-association wherever you like, as long as its outside your app’s directory (e.g. your desktop works well), and add the following content:

{
  "applinks": {
    "apps": [],
    "details": [
    {
      "appID": "KFCNEC27GU.com.razeware.UniveralLinksTutorial",
      "paths": ["*"]
    }
    ]
  }
}

The applinks tag determines which apps are associated with the web site. Leave the apps value as an empty array. Inside the details tag is an array of dictionaries for linking appIDs and URL paths. For simplicity, you use the * wildcard character to associate all of this web site’s links with the UniversalLinks app. You can limit the paths value to specific folders or file names.

The appID consists of your team ID combined with the app’s bundle ID. The team ID listed above belongs to the Ray Wenderlich team, but you’ll need to use the identifier for your own account.

Apple assigned you a team ID when you created your Apple developer account. You can find it in the Apple developer center. Log into the web site, click on Membership, then look for Team ID in the Membership Information section.

Developer Account Summary

Developer Account Summary

You can find the app’s bundle ID via Xcode’s Targets ▸ UniversalLinks ▸ General tab:

Bundle ID

Now that the apple-app-site-association is complete, it’s time to upload it to the web server.

Again, you must have “write access” to the web site to do this. For the sake of the tutorial, you can refer to the finished web site, which already contains this file.

If you want to ensure it is there, open http://rw-universal-links-final.herokuapp.com/apple-app-site-association. You’ll see that it matches the information shown above.

Great! The app knows about the web site, and the web site knows about the UniversalLinks app. It’s time for the final step: adding app logic to handle when a user taps a universal link.

Handling Universal Links

Now that the app and the web site are officially aware of each other, all the app needs is code to handle the link when it’s called.

Open AppDelegate.swift and add the following helper method:

func presentDetailViewController(_ computer: Computer) {
  let storyboard = UIStoryboard(name: "Main", bundle: nil)
  
  guard 
    let detailVC = storyboard
      .instantiateViewController(withIdentifier: "DetailController")
        as? ComputerDetailController,
    let navigationVC = storyboard
      .instantiateViewController(withIdentifier: "NavigationController")
        as? UINavigationController 
  else { return }
  
  detailVC.item = computer
  navigationVC.modalPresentationStyle = .formSheet
  navigationVC.pushViewController(detailVC, animated: true)
}

This method opens the ComputerDetailController and displays the Computer parameter’s information. This provides you with a clean way of navigating to a specific single board computer. You’ll use this method next.

Right after the previous method, add the following delegate method:

func application(
  _ application: UIApplication,
  continue userActivity: NSUserActivity,
  restorationHandler: @escaping ([UIUserActivityRestoring]?
) -> Void) -> Bool {
  
  // 1
  guard userActivity.activityType == NSUserActivityTypeBrowsingWeb,
    let url = userActivity.webpageURL,
    let components = URLComponents(url: url, resolvingAgainstBaseURL: true) else {
      return false
  }
  
  // 2
  if let computer = ItemHandler.sharedInstance.items
    .filter({ $0.path == components.path}).first {
    presentDetailViewController(computer)
    return true
  }
  
  // 3
  if let webpageUrl = URL(string: "http://rw-universal-links-final.herokuapp.com") {
    application.open(webpageUrl)
    return false
  }
  
  return false
}

iOS calls this method whenever the user taps a universal link related to the app. Here’s what each step does:

  1. First, you verify that the passed-in userActivity has expected characteristics. Ultimately, you want to get the path component for the activity. Otherwise, you return false to indicate that the app can’t handle the activity.
  2. Using the path, you look for a known computer that matches it. If you find one, you present the detail view controller for it and return true.
  3. If you can’t find a computer that matches the path, you instruct the application to open the URL, which will use the default system app instead — most likely Safari. You also return false here to indicate that the app can’t handle this user activity.

Testing the Links

As discussed above, there isn’t a great way to test whether the universal links work in the tutorial app, but it’s important to understand the expected outcome for when you implement universal links in your own apps.

If you had implemented this in your own app, you would be able to email yourself a link (e.g., https://rw-universal-links-final.herokuapp.com/arduino.html), tap it, and verify that the app shows the correct page.

Remember that universal links can be triggered from other places as well, such as within a UIWebView, WKWebView, SFSafariViewController, Notes or directly within Safari.

Where to Go From Here?

You can download the completed version of the project using the Download Materials button at the top or bottom of this tutorial. And you’ll find the finished web site here: https://rw-universal-links-final.herokuapp.com.

Congratulations! You’ve learned a lot about how to implement universal links in iOS. You’re now ready to apply this concept to your own app and create “real” universal links — you can do it!

Want to dig deeper into this topic? Check out the section on universal links (Chapter 3) in iOS 9 by Tutorials.

You can also find additional reference material directly from Apple’s documentation.

To test your implementation of universal links on your own web site, you can use this tool or Apple’s own link validator, both of which can point out possible problems in your setup.

If you have questions or comments, feel free to jump into the forum below and ask away!

Contributors

Comments