Universal Links Logo

Implementing Universal Links – The Basics

This entry is part 1 of 12 in the series Universal Links

This series will walk through the steps needed to implement URL Schemes (Deep Links), and Universal Links in iOS/iPadOS apps (Using Standard UIKit App Delegate, UIKit Scene Delegate, and SwiftUI). This post is an introduction, and will establish our baseline.

The Big Picture

Universal Links are an option that Apple has provided its app developers, since 2015 (iOS9).

Quite simply, it’s a link that will open any Web page, but, if executed on a device with a specially-tuned app, will, instead, open the app, in the same manner as the standard URL Scheme (AKA “Deep Links”).

“Deep Links” are something that iOS apps have been able to leverage for a very long time (Deep Links have been available since iOS2). They aren’t especially difficult to add to your project, and give the ability to have a URL that is a “one-tap” direct access to the app.

In my own work, I use Universal Links as a “fallback.” The app can send an email that contains a link to a user’s profile page, for example. In my app, the user page is only visible (and useful) to those who have the app installed. The app needs to be opened, and the user’s profile page is then shown. However, if the app is not installed, a universal link, instead, redirects the user to the Apple App Store page for that app.

A redirect is not always what folks may want. For example, there may be a Web interface for the app, and we may want to open the user’s profile page on the Web, in lieu of the app they don’t have.

If I used the classic “Deep Link” service, the app would open fine, if it was installed, but the user would get an ugly error, if it was not.

Advantages

  1. One Link to Rule Them All
    No need to have two different links; one, to the app, and the other, to a Web page that provides the information in a more generic fashion (or an App Store link).
  2. No Ugly “Unable to Open Resource” Error
    A deep link will cause the user to see a fairly unattractive error, if the app is not installed. With a Universal Link, you simply get a Web page, if the app is not available.
  3. No “Do You Want to Open the XXX App?” Alert
    With Deep Links, selecting a link always results in a confirmation alert, asking if you want to open the app. If you select a Universal Link, it does not.
  4. You Can Introduce New Users to Your App
    If the user is sent an email, for example, with a universal link, then that link might actually be a link to the App Store.
    This is a way to recruit new users in the best possible way; so they can access information that they actually want to see.
    No need to “push” users to your app. The link will “pull” them to you.

Disadvantages

  1. More Involved Implementation
    You’ll need to jump through a few extra hoops, to implement Universal Links, compared to Deep Links. You’ll need a Web server and DNS provider, as well as the app development environment. You’ll need someone that knows how to deal with the Web site, and someone to keep it running, after the app has been released.
    This will add additional resources, costs, documentation, planning, security, and support infrastructure to a project.
  2. More Places for Things to Go Wrong
    Since this requires a Web site, as well as the app, there will be a couple of extra “weak spots,” that could cause issues.
  3. Possible Security Issues
    If the app is installed on your device, no network activity happens outside the device, and the app is opened quickly; but if the app is not installed, it’s likely that your device will make an external network call. This might be considered a possible security issue. Additionally, if the Web site is compromised, it could create a vector for network intrusion or malware delivery.
    A Deep Link simply causes an on-device error, and nothing is sent anywhere. If someone without the app gets the link, it likely won’t do them much good.
  4. Funky Safari Behavior
    If you enter a Deep Link into the Safari Location Text item, it immediately tries to resolve it, and you get the “Do You Want to Open the XXX App?” alert. If you enter a Universal Link, it actually goes out and opens the Web page, displaying a banner that asks if you want to open the same page in the app. This may not be ideal behavior, but it also only happens with direct input into the Location Text. Selecting an existing Universal Link on a Safari-displayed page, opens the app directly.

In my own experience, with the types of apps that I’ve written, Universal Links are a “no brainer.” That said, there’s definitely a need to consider the downsides, for some implementations.

The Baseline

Just to set the scene:

  • Xcode Version: This post was written, using Xcode 15. All screenshots and testing, has been done, using Xcode (and Simulator) 15.
  • MacOS Version: I am using MacOS Sonoma 14.5.
  • iOS/iPadOS Version: All testing and samples are being done with iOS/iPadOS 17.4.
  • Web Server: The Web server I’m using is a standard-issue, shared LAMP hosting (Linux/MySQL). The file, itself, is HTML, and uses simple JavaScript to provide the functionality. Almost any hosting will do. The one I use is provided by A2Hosting (the “Turbo Boost” option).
  • DNS: The Web server provides its own DNS, so subdomains are being set up and maintained by the local CPanel.
  • HTTPS: The server provides HTTPS, using AutoSSL.
  • Swift Version: The version of the Swift compiler used for this, is the current version for Xcode 15 (Swift 5). We are not leveraging anything fancy.
  • Visual: I use Dark Mode, with a yellow selection, but my text files tend to be Default (Light), using the Intel One Mono font, and purple selection.
  • Simulator: All screenshots will be done, using the Xcode Simulator. We’ll usually use a standard iPhone15Pro (Dynamic Island) simulator.
  • Accompanying Code: There will be a GitHub Repo, containing all of the examples we will work with. We will use a series of releases to track the lesson. This is the starting point.

Baseline Project Manifest

If you open ImplementingUniversalLinks.xcworkspace in Xcode, you will see the following Navigator manifest (if you open every directory), you will also see three targets, and three schemes (for running):

We will be editing the following three files. We’ll be leaving all the others alone:

We’ll also be adding one more file to the Web portion, as the project progresses, but we’re not there, yet.

If you run any of the three targets, you will see the following screens:

This is a very simple, 1-view app, with three “traffic lights.” The three gray circles will represent three states of the app. If the app is in normal (“off”) state, all three are gray.

If it is in “stop” state, the leftmost circle is red, if in “caution” state, the center circle is yellow, and, if in “go” state, the rightmost circle is green:

The “index.html” files in the “Web” subdirectory, will do the same. In that case, a Javascript function will parse the input URI, and set the correct light to the correct color. You can test this, by calling each file through Safari, and appending “?off“, “?stop“, “?caution“, and “?go” to the URI. Note that they are case-sensitive. The query arguments should all be in lowercase.

That’s all there is, to it. We’ll be sending URLs to the app, telling it to switch on certain lights.

In my initial setup, I have the index.html files set up on my server, and you can see that in action, here. We won’t be coming back to this for a while:

Now that this has been established, let’s move on to the lesson, itself.