App Links (Android App Links) and Universal Links (iOS Universal Links) are platform-specific deep linking standards that open native apps directly from web URLs without user prompts. App Links use Android's Intent Filter verification with Digital Asset Links, while Universal Links rely on iOS's apple-app-site-association file hosted on your domain. Both require HTTPS domains and proper server configuration. The key difference: App Links work on Android 6.0+, Universal Links on iOS 9+, and they're not interchangeable—you need both for cross-platform coverage.
Understanding App Links vs Universal Links: Platform-Specific Standards
App Links and Universal Links solve the same problem using different technical implementations. When a user taps a link in an email, SMS, or web page, these standards allow the URL to open directly in your native app instead of a mobile browser—assuming the app is installed.
Here's the mental model that matters:
- Android App Links: Google's standard requiring verified domain ownership through a JSON file at
/.well-known/assetlinks.json - Universal Links: Apple's standard requiring verified domain ownership through a JSON file at
/.well-known/apple-app-site-association
Both are verified deep links—the operating system validates that you own both the domain and the app before allowing seamless handoff. This prevents malicious apps from hijacking your web traffic.
Without verification, you're stuck with URI schemes (myapp://), which trigger confirmation dialogs, fail silently if the app isn't installed, and don't work in many contexts like Chrome Custom Tabs or in-app browsers.
Key Technical Differences
| Feature | Android App Links | iOS Universal Links |
|---|---|---|
| OS Support | Android 6.0+ (API 23+) | iOS 9.0+ |
| Verification File | assetlinks.json |
apple-app-site-association |
| File Location | /.well-known/assetlinks.json |
/.well-known/apple-app-site-association or root |
| Content-Type | application/json |
application/json or application/pkcs7-mime |
| Requires HTTPS | Yes | Yes |
| Fallback Handling | Opens in browser automatically | Opens in Safari automatically |
| Verification Timing | App install + periodic checks | App install + updates |
The verification files serve the same purpose but have different syntax. Google wants proof your app should handle your domain. Apple wants the same proof, formatted differently.
How App Links and Universal Links Work
Both systems follow a similar flow, but implementation details diverge significantly.
Android App Links Implementation
Step 1: Configure Intent Filters
In your AndroidManifest.xml, declare which URLs your app handles:
<activity android:name=".ProductActivity">
<intent-filter android:autoVerify="true">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data
android:scheme="https"
android:host="example.com"
android:pathPrefix="/products" />
</intent-filter>
</activity>
The android:autoVerify="true" attribute triggers domain verification at install time.
Step 2: Host Digital Asset Links File
Place this at https://example.com/.well-known/assetlinks.json:
[{
"relation": ["delegate_permission/common.handle_all_urls"],
"target": {
"namespace": "android_app",
"package_name": "com.example.app",
"sha256_cert_fingerprints": [
"14:6D:E9:83:C5:73:06:50:D8:EE:B9:95:2F:34:FC:64:16:A0:83:42:E6:1D:BE:A8:8A:04:96:B2:3F:CF:44:E5"
]
}
}]
The sha256_cert_fingerprints must match your app's signing certificate. Android validates this when the app is installed.
Step 3: Handle Incoming Links
In your Activity:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
handleIntent(intent)
}
override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent)
handleIntent(intent)
}
private fun handleIntent(intent: Intent) {
val appLinkData: Uri? = intent.data
if (appLinkData != null) {
val productId = appLinkData.lastPathSegment
// Navigate to product screen
}
}
iOS Universal Links Implementation
Step 1: Configure Associated Domains
In Xcode, add your domain to the Associated Domains capability:
applinks:example.com
This goes in your entitlements file (YourApp.entitlements):
<key>com.apple.developer.associated-domains</key>
<array>
<string>applinks:example.com</string>
</array>
Step 2: Host AASA File
Create https://example.com/.well-known/apple-app-site-association (no file extension):
{
"applinks": {
"apps": [],
"details": [
{
"appID": "TEAMID.com.example.app",
"paths": ["/products/*"]
}
]
}
}
The appID combines your Team ID (from Apple Developer account) with your bundle identifier. iOS downloads this file when your app is installed.
Step 3: Handle Universal Links
In your AppDelegate or SceneDelegate:
func application(_ application: UIApplication,
continue userActivity: NSUserActivity,
restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
guard userActivity.activityType == NSUserActivityTypeBrowsingWeb,
let url = userActivity.webpageURL else {
return false
}
// Parse URL and navigate
if url.pathComponents.contains("products") {
let productId = url.lastPathComponent
// Navigate to product screen
}
return true
}
For SwiftUI apps using scenes:
WindowGroup {
ContentView()
.onOpenURL { url in
// Handle universal link
}
}
Common Mistakes and Edge Cases
After implementing both systems in production, here are the issues that consistently trip up developers:
1. HTTPS Misconfiguration
Both platforms require valid HTTPS certificates. Self-signed certificates, expired certificates, or certificate chain issues will silently break verification. Use tools like SSL Labs to validate your setup before debugging app code.
Edge case: If you're using a CDN or reverse proxy (CloudFlare, Fastly), ensure the verification files bypass caching and transformations. CDNs sometimes modify JSON responses, breaking signature verification.
2. Android Certificate Fingerprint Mismatches
The most common Android App Links failure: using debug keystore fingerprints in production or forgetting to add release certificate fingerprints.
Get your release fingerprint:
keytool -list -v -keystore release.keystore
If you use Google Play App Signing, you need both your upload key fingerprint and the app signing key fingerprint from Play Console. Add both to assetlinks.json.
3. iOS Path Matching Gotchas
Universal Links path patterns are finicky:
"/products/*"matches/products/123but NOT/products"/products*"matches both/productsand/products/123- Query parameters are ignored in path matching
Exclusion patterns work with NOT:
"paths": ["/buy/*", "NOT /buy/exclude/*"]
Test thoroughly because iOS caches AASA files aggressively.
4. Testing in Wrong Contexts
Universal Links don't work when tapping links on the same domain in Safari. If you're on example.com and tap a link to example.com/products/123, Safari opens it in the browser, not your app. This is intentional—Apple assumes users want to stay in Safari.
Test from different contexts:
- Notes app
- Messages
- Third-party apps (Slack, Email)
- Safari Reader Mode
On Android, test from Gmail, Chrome (not same-domain), and SMS. App Links behave differently in WebView contexts.
5. Subdomain and Multi-Domain Complications
Each subdomain needs its own verification file. If you support shop.example.com and example.com, you need:
https://example.com/.well-known/assetlinks.jsonhttps://shop.example.com/.well-known/assetlinks.json
On iOS, add each subdomain to Associated Domains:
applinks:example.com
applinks:shop.example.com
Wildcard subdomains are supported on iOS (applinks:*.example.com) but require careful AASA configuration.
6. Link Shorteners Breaking Verification
If you use URL shorteners like bit.ly or generic shorteners, App Links and Universal Links won't work unless the shortener domain is also configured. The OS validates the shortened URL's domain, not the final destination.
Solution: Use a URL shortener that supports custom domains for deep linking, allowing you to host verification files on your branded short domain.
Best Practices for Production Systems
Verify Early, Test Often
Use Google's Statement List Generator and Tester:
https://developers.google.com/digital-asset-links/tools/generator
For iOS, use Apple's AASA validator or test directly:
https://app-site-association.cdn-apple.com/a/v1/example.com
This shows you exactly what iOS cached for your domain.
Monitor Verification File Availability
Set up uptime monitoring for your assetlinks.json and AASA files. If these go down or return 404s, new app installs won't establish verified links.
Check that:
- Files return
200 OK - Content-Type is
application/json - No redirects occur (Android may follow 301/302, iOS won't)
- Response size is under 128 KB (iOS limit)
Version Your AASA Files Carefully
iOS caches AASA files for days or weeks. When you update path patterns, existing users may not see changes immediately. Plan for gradual rollout or use server-side routing to handle both old and new patterns during transitions.
Implement Fallback Logic
Even with perfect configuration, App Links and Universal Links can fail due to:
- User explicitly choosing "Open in Browser"
- Corporate network proxies stripping headers
- OS bugs or version-specific issues
Always include a web fallback that handles the same deep link parameters. Your web page should:
- Attempt a final deep link with custom URI schemes (fallback of fallback)
- Show app store badges
- Display meaningful content for web users
Track Link Performance
Instrument your deep link handlers to track:
- App opens via verified links vs URI schemes vs direct launch
- Failed deep link attempts (users who landed on web instead)
- Time-to-handle metrics
Use link-level analytics to understand where users drop off in the deep link flow.
When NOT to Use App Links or Universal Links
1. You Don't Control the Domain
If you're using third-party platforms (Shopify, WordPress.com, hosted solutions) where you can't upload custom files to /.well-known/, verified deep links won't work. You need full control over your domain's web server.
2. Your App Isn't Listed Yet
Both systems require your app to be installed from official app stores. During beta testing with TestFlight or internal builds, Universal Links work, but if you're using sideloaded APKs or enterprise distribution, verification may fail.
3. Cross-App Communication
If you're building deep links for other apps to call yours (like payment callbacks or OAuth redirects), URI schemes (yourapp://callback) are more reliable. App Links and Universal Links are designed for web-to-app transitions, not app-to-app.
4. Immediate Testing Needs
Setting up verified links requires domain access, certificate management, and app store builds. If you need to test deep linking logic quickly, start with URI schemes. Migrate to verified links before production launch.
5. Web-Only Flows
If your feature is intentionally web-only (like password reset flows that should never open in-app), exclude those paths from your verification files. Don't assume every link should deep link.
How App Links and Universal Links Fit Into Modern Deep Linking Systems
In 2025, verified deep links are table stakes, but they're only one piece of a complete deep linking strategy.
The Verified Link Foundation
App Links and Universal Links handle the "installed app" scenario perfectly. When a user taps a link and has your app, these standards provide instant, seamless handoff. This is critical for:
- Email campaigns to existing users
- Push notification fallbacks
- Social media post attribution
- Referral program links
Deferred Deep Links for New Users
Verified links don't solve the "app not installed" problem. When users don't have your app, the link opens in a browser. This is where deferred deep linking becomes essential.
A complete system combines:
- Universal Links / App Links for installed users (instant open)
- Smart web pages that detect app installation status
- Deferred deep links that preserve context through app install
- Attribution tracking to measure conversion across the journey
Platforms like Smler implement this full stack. When you create a deferred deep link, the system:
- Serves verified AASA and assetlinks files for your custom domain
- Routes installed users directly to your app via App Links/Universal Links
- Sends uninstalled users to the app store with preserved deep link data
- Passes original link context to your app on first launch
Analytics and Attribution Layer
Raw App Links and Universal Links don't provide click tracking, geographic data, or device information. Wrapping them in a URL shortener with link-level analytics gives you visibility into:
- Click-through rates before app opens
- Platform split (iOS vs Android vs desktop)
- Geographic performance
- Time-to-conversion metrics
Multi-Platform Routing
Production deep linking systems need device-based routing. A single shortened URL should:
- Open Android app via App Links on Android devices
- Open iOS app via Universal Links on iOS devices
- Redirect desktop users to a landing page
- Handle edge cases (Kindle, smart TVs, etc.)
Implementing this manually requires significant infrastructure. Smart URL shorteners handle platform detection, fallback logic, and routing automatically.
Compliance and Brand Control
For SMS campaigns, especially in regulated markets, you need compliance-ready short URLs with custom domains and header support. Generic shorteners (bit.ly, tinyurl) don't support the domain verification files needed for App Links and Universal Links.
Using branded short domains gives you:
- Full control over verification files
- Brand consistency in links
- Trust signals for users
- SEO benefits from your own domain
Frequently Asked Questions
Can I use both App Links and Universal Links for the same URL?
Yes, and you should. A single https://example.com/products/123 URL can work as both an Android App Link and iOS Universal Link. Host both assetlinks.json and apple-app-site-association files on the same domain. The operating systems check their respective files independently.
Why do my Universal Links stop working after tapping "Open in Safari"?
iOS remembers user preference. If a user long-presses a Universal Link and chooses "Open in Safari" (or taps the top-right breadcrumb to return to Safari), iOS disables Universal Links for that domain until the user manually re-enables them. There's no programmatic fix—this is intentional user control. To re-enable, the user must long-press a link and choose "Open in [App Name]".
Do App Links work in WebView or Chrome Custom Tabs?
Android App Links behavior varies by context. In Chrome Custom Tabs, they typically work. In WebView components, they often don't—the link opens in the WebView browser instead of triggering the app. This affects in-app browsers used by Facebook, Twitter, and Instagram. For reliable deep linking from social apps, consider implementing alternative strategies like QR codes that open in system browsers.
How long does it take for iOS to verify my AASA file after I update it?
iOS fetches AASA files during app installation and updates. For already-installed apps, iOS may cache the file for several days. Apple's CDN also caches it. In practice, expect 24-48 hours for changes to propagate to all users. Uninstalling and reinstalling your app forces immediate re-fetch during testing, but don't expect real users to do this.
Can I debug why my App Links aren't working on Android?
Yes. Use ADB to check verification status:
adb shell pm get-app-links com.example.app
This shows which domains are verified. For detailed logs:
adb shell dumpsys package domain-preferred-apps
Look for your package name and check verification state. If it shows "none" or "legacy_failure", there's a problem with your assetlinks.json file or certificate fingerprints.
Summary: Choosing the Right Approach
App Links and Universal Links are not competing standards—they're complementary platform requirements. If you're building a cross-platform mobile app, you need both. There's no either/or decision.
Key takeaways:
- App Links are Android's verified deep linking standard; Universal Links are iOS's equivalent
- Both require HTTPS domains, server-hosted verification files, and proper app configuration
- Implementation differs significantly, but the end-user experience is identical: seamless web-to-app transitions
- Common failures stem from certificate mismatches, caching issues, and testing in wrong contexts
- Verified links are essential but insufficient alone—combine with deferred deep linking, analytics, and smart routing
For production systems, consider using a deep linking platform that handles verification file hosting, multi-platform routing, and analytics automatically. This lets you focus on app features instead of infrastructure edge cases.
When implementing from scratch, start with one platform to validate your verification file setup, then expand to the second. Test across multiple apps and contexts before launching to users. And always implement web fallbacks—no deep linking system is 100% reliable across all environments.
Published with LeafPad