Deep Linking
Migrating from Custom URI Schemes to Universal and App Links
Learn the architectural differences between legacy custom schemes and modern verified links to improve app security and user reliability.
In this article
Architecting Modern Verified Links
The architecture of modern deep linking depends on a secure association between a web domain and a mobile application bundle. On iOS, this is implemented via Universal Links, while Android uses the App Links protocol. Both systems require the developer to host a JSON configuration file on their web server in a specific hidden directory.
The operating system downloads these files when the app is first installed or updated. By parsing these files, the OS builds a local database of URL patterns that are authorized to bypass the browser. This prevents malicious apps from intercepting sensitive links intended for your platform.
- Domain verification requires an active HTTPS connection with a valid certificate.
- Configuration files must be hosted in the .well-known directory of the web server.
- The MIME type for these files should be application/json to ensure proper parsing by the OS.
When a user clicks a verified link, the OS intercepts the request before it reaches the browser. It compares the URL against the patterns defined in its local database. If a match exists, the app is launched immediately with the URL passed into the application delegate or activity for further routing.
Configuring the Server-Side Handshake
For iOS, the configuration file is named apple-app-site-association and contains an array of application identifiers and allowed paths. It is important to note that this file must not have a file extension. The file lists the specific bundle IDs that are allowed to handle links for the domain.
Android uses a similar file named assetlinks.json which contains the package name and the SHA-256 fingerprint of the app signing certificate. This ensures that only a version of the app signed by your official developer key can claim the links. This prevents developers from spoofing links during testing or in third-party app stores.
1[
2 {
3 "relation": ["delegate_permission/common.handle_all_urls"],
4 "target": {
5 "namespace": "android_app",
6 "package_name": "com.example.storeapp",
7 "sha256_cert_fingerprints": [
8 "AB:CD:12:34:56:78:90:EF:AB:CD:12:34:56:78:90:EF:AB:CD:12:34:56:78:90:EF:AB:CD:12:34:56:78:90:EF"
9 ]
10 }
11 }
12]Implementing Deep Link Logic in Code
Once the OS identifies a matching link, it triggers a specific entry point within your application code. On Android, this is handled through the Intent system. The Activity responsible for deep linking must be declared in the manifest with an intent-filter that includes the autoVerify attribute set to true.
In iOS, the routing logic typically resides in the SceneDelegate or AppDelegate depending on the age of the project. Modern SwiftUI applications use the onOpenURL modifier to handle incoming links directly within the view hierarchy. Regardless of the framework, the logic involves parsing the URL to extract identifiers like product IDs or session tokens.
1override fun onCreate(savedInstanceState: Bundle?) {
2 super.onCreate(savedInstanceState)
3
4 // Check if the activity was started by a deep link
5 val intentData = intent?.data
6 if (intentData != null) {
7 val productId = intentData.lastPathSegment
8 // Route the user to the specific product view
9 navigateToProduct(productId)
10 }
11}
12
13override fun onNewIntent(intent: Intent?) {
14 super.onNewIntent(intent)
15 // Handle links when the activity is already in the foreground
16 setIntent(intent)
17 processDeepLink(intent?.data)
18}Handling the app lifecycle is a critical part of the implementation. If the app is already running in the background, the system might not call the standard initialization methods. Developers must implement specialized callbacks like onNewIntent on Android to ensure the deep link is processed even when the app is already warm.
Parsing and Routing Best Practices
Parsing should always be defensive and account for malformed URLs. It is a common mistake to assume the URL structure will always be perfect. Use robust URL parsing libraries to extract query parameters and path segments without crashing the app.
A centralized router is highly recommended for managing deep links. Instead of putting routing logic in every individual screen, create a single coordinator or router class. This class takes a URI as input and determines the correct navigation stack and destination view to present to the user.
Security, Testing, and Edge Cases
Security is a primary concern when dealing with deep links because they can be used to bypass traditional authentication flows. You should never assume that the presence of a deep link implies a verified user session. Always validate session tokens or user permissions before displaying sensitive data triggered by a link.
Testing deep links requires simulating various application states. You must test what happens when the app is completely closed (cold start) versus when it is already running in the background (warm start). Use command-line tools like adb for Android or xcrun for iOS to trigger URLs without needing to send real emails or push notifications.
- Verify that the web server serves the association file without any redirects.
- Ensure the Content-Type header is strictly application/json.
- Check the size of the association file, as mobile OSs often have strict limits around 128KB.
One common pitfall is the failure of the OS to update its local link database. If you change your AASA or assetlinks file, the OS may not see the changes until the user updates the app or waits for a background refresh. During development, you may need to uninstall and reinstall the app multiple times to force a re-verification.
Deferred Deep Linking
Standard deep linking only works if the application is already installed on the device. When the app is missing, the user is typically sent to the App Store or the website. Deferred deep linking solves this by passing the original link data through the installation process.
This usually requires a third-party attribution service that tracks the user from the initial click to the first app launch. The service identifies the user's device and provides the pending deep link data to the app as soon as it is opened for the first time. This ensures a smooth onboarding experience for new customers.
