If you've built an iOS app that needs to open from a link, you've run into a choice: use a custom URI scheme like myapp:// or use Universal Links over HTTPS. Both get you to the same basic outcome, but they work very differently under the hood and carry different trade-offs for security, user experience, and reliability.
This article breaks down how each approach works, where each one falls short, and when you should choose one over the other. For a comprehensive introduction to Universal Links, see our complete Universal Links guide, and for a deep dive into custom schemes specifically, see the custom URL schemes guide.
How Custom URI Schemes Work
A custom URI scheme is a URL prefix that your app registers with iOS to claim as its own. When any link starting with that prefix is tapped anywhere on the device, iOS routes it directly to your app.
Registering a URI Scheme
You register a custom scheme in your app's Info.plist file:
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLName</key>
<string>com.yourcompany.yourapp</string>
<key>CFBundleURLSchemes</key>
<array>
<string>myapp</string>
</array>
</dict>
</array>
With that in place, any link that starts with myapp:// will open your app if it's installed. If it's not installed, iOS shows an error dialog — there's no built-in fallback to a website or the App Store.
Handling Incoming URI Scheme Links
In your AppDelegate (or SceneDelegate), you handle the incoming URL:
func application(
_ app: UIApplication,
open url: URL,
options: [UIApplication.OpenURLOptionsKey : Any] = [:]
) -> Bool {
guard url.scheme == "myapp" else { return false }
// Parse the path and query parameters
let path = url.host ?? ""
let components = URLComponents(url: url, resolvingAgainstBaseURL: false)
let params = components?.queryItems
// Route to the appropriate screen
handleDeepLink(path: path, params: params)
return true
}
The URL structure is flexible. You can use myapp://product/123, myapp://checkout?cart=abc, or any path convention you choose.
Limitations of URI Schemes
URI schemes have three problems that become significant at production scale:
No verification. Any app can register any URI scheme. If two apps both register myapp://, iOS picks one (usually the app installed most recently) and the other gets nothing. There's no authority that enforces uniqueness.
No fallback. If the app isn't installed and a user taps a myapp:// link, iOS shows an error alert that says "Safari cannot open the page because the address is invalid." The user has no path to install the app or reach your website. The link is a dead end.
App Store links are the only workaround. Some developers detect failures using a timer hack — open the URI scheme and simultaneously open an App Store URL, then cancel the App Store redirect if the app opens. This approach is fragile and was never supported by Apple.
For a deeper look at how these limitations affect real deep linking use cases, see the deep linking concepts overview.
How Universal Links Work
Universal Links are HTTPS URLs that iOS recognizes as belonging to your app. Instead of registering a scheme in Info.plist, you establish a verified association between your app and your domain. When that association exists, iOS opens matching links directly in your app without touching the browser.
This verification step is what makes Universal Links meaningfully different from URI schemes.
The Apple App Site Association File
The verification happens through a file called apple-app-site-association (AASA) that you host at the root or .well-known/ directory of your domain:
https://yourdomain.com/.well-known/apple-app-site-association
The file is JSON with no file extension:
{
"applinks": {
"apps": [],
"details": [
{
"appIDs": ["TEAMID.com.yourcompany.yourapp"],
"components": [
{
"/": "/product/*",
"comment": "Match product pages"
},
{
"/": "/checkout/*",
"comment": "Match checkout flow"
}
]
}
]
}
}
The appIDs field combines your Apple Team ID with your app's bundle identifier. The components array specifies which URL paths should open the app versus fall through to the browser.
Apple's servers fetch and cache this file when a user installs your app. iOS then uses the cached file to decide whether an HTTPS link should open your app or Safari. The Apple Developer documentation on Universal Links covers the full AASA specification and the exact fetching behavior.
Enabling Universal Links in Xcode
In your app's entitlements, add the com.apple.developer.associated-domains key with your domain:
<key>com.apple.developer.associated-domains</key>
<array>
<string>applinks:yourdomain.com</string>
</array>
In Xcode, this is under Signing and Capabilities > Associated Domains. Add an entry like applinks:yourdomain.com.
Handling Universal Links in Code
Universal Links come in through application(_:continue:restorationHandler:):
func application(
_ application: UIApplication,
continue userActivity: NSUserActivity,
restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void
) -> Bool {
guard userActivity.activityType == NSUserActivityTypeBrowsingWeb,
let url = userActivity.webpageURL else {
return false
}
let components = URLComponents(url: url, resolvingAgainstBaseURL: true)
let path = components?.path ?? ""
let params = components?.queryItems
handleDeepLink(path: path, params: params)
return true
}
If you're using UIScene, handle it in scene(_:continue:) instead.
The Fallback Advantage
When a user taps a Universal Link and your app isn't installed, iOS opens the URL in Safari. That means you can serve a proper web page at that URL with an App Store redirect, a web version of the content, or a smart banner prompting installation. The user experience doesn't break. See how Universal Links handle fallback routing for more on building that fallback layer.
Security: A Clear Difference
URI scheme hijacking is a real attack vector. Because any app can register any scheme, a malicious app can register the same myapp:// scheme as your app and intercept links that were meant for you. On a device where both apps are installed, iOS behavior is unpredictable.
This isn't theoretical. OAuth and authentication flows that use URI schemes for redirect callbacks are particularly exposed. If a malicious app intercepts the myapp://oauth-callback?code=... redirect, it can steal the authorization code.
For a thorough analysis of these attack vectors and how to defend against them, see our guide on deep linking security.
Universal Links are not vulnerable to this attack. iOS only opens a Universal Link in your app if you own the domain (you can serve the AASA file over HTTPS) and your app is code-signed with the Apple Team ID in that file. No other app can impersonate your domain under those conditions.
For authentication flows, Apple explicitly recommends using Universal Links (or ASWebAuthenticationSession) rather than URI schemes precisely because of this vulnerability.
User Experience Differences
The failure mode matters. When a URI scheme link fails, users see a confusing error dialog from Safari that gives them no useful next step. They can't install the app from that point, they can't reach a web page, and they don't know what to do.
When a Universal Link falls through to the web, you control the experience. You can redirect to the App Store, show a web version of the content, or display a page that explains what happened and gives users options. That fallback page is your page, with your design and copy.
There's one catch with Universal Links: if a user taps a Universal Link inside Safari on iOS, they see a banner at the top of the screen that lets them open the app. But if they tap a Universal Link within your app (using WKWebView or SFSafariViewController), Universal Links are disabled by default. You have to open the URL using UIApplication.shared.open() rather than loading it in a web view for the routing to work.
Comparison Table
| Factor | URI Schemes | Universal Links |
|---|---|---|
| Registration | Info.plist only |
AASA file + entitlements |
| Uniqueness guarantee | None | Verified via domain ownership |
| Hijacking risk | Yes | No |
| Fallback if app not installed | Error dialog | Web page (your control) |
| Works in email clients | Usually | Yes |
| Works in Messages | Yes | Yes |
| Works in third-party browsers | Varies | Yes (opens app directly) |
| Requires HTTPS domain | No | Yes |
| iOS version support | All | iOS 9+ |
| Setup complexity | Low | Medium |
When to Use URI Schemes
URI schemes still make sense in one specific context: inter-app communication within a controlled environment.
If you're building a suite of apps that all belong to your organization and you need one app to open another (a companion app, a separate checkout experience, an enterprise tool), URI schemes are simpler to set up and there's no hijacking risk because you control both apps on the device.
URL scheme registration via canOpenURL also gives you a lightweight way to check if a specific app is installed, which Universal Links don't support. You have to add the scheme to LSApplicationQueriesSchemes in Info.plist, but then you can call UIApplication.shared.canOpenURL(url) to check before attempting to open.
Some payment and authentication SDKs also use URI schemes for their redirect flows because they predate Universal Links or because they need to support older iOS versions. If you're integrating an SDK that requires it, you don't have a choice.
For everything else, prefer Universal Links.
When to Use Universal Links
Universal Links are the right choice for any link that might reach a user who doesn't have your app installed. That covers:
- Marketing links in email campaigns
- Social media posts and profiles
- QR codes in physical spaces
- Links shared between users in other apps (Messages, WhatsApp, etc.)
- Links from your website to your app
- Paid ad campaigns with deep link destinations
The web fallback is what makes Universal Links work at scale. You don't know whether the person who taps your link has your app installed. URI schemes bet that they do. Universal Links handle both cases gracefully.
Platforms like Tolinku generate Universal Link-compatible deep links and manage the AASA configuration, routing logic, and web fallback pages so you don't have to build that infrastructure yourself.
Putting It Together
URI schemes and Universal Links solve overlapping problems with different approaches. URI schemes are simple and fast to implement, but they carry meaningful security risks, produce poor failure experiences, and have no path to the web when the app isn't installed.
Universal Links require more setup: you need an HTTPS domain, an AASA file with the correct format, Xcode entitlements, and a web fallback page. But they give you domain-verified security, a controlled fallback experience, and consistent behavior across email clients, browsers, and messaging apps.
For internal app-to-app communication in controlled environments, URI schemes are fine. For any link that touches an end user who might or might not have your app installed, Universal Links are the right foundation. If you also support Android, the same verified-link approach applies there; see our complete guide to Android App Links for the equivalent setup.
The Apple Developer documentation on supporting Universal Links has the authoritative reference for AASA format and entitlement configuration. The deep linking features overview covers how to layer routing logic, analytics, and deferred deep linking on top of Universal Links once the basics are working.
Get deep linking tips in your inbox
One email per week. No spam.