AutoVerify = true is required on the IntentFilter for App Links (not
just deep links). Without it, Android shows a disambiguation dialog instead
of opening your app directly.
Handle intent in both OnCreate and OnNewIntent.OnCreate fires for cold starts; OnNewIntent fires when the app is already
running. Missing either means links silently fail in one scenario.
SHA-256 fingerprint must match the signing key used for the build you're
testing. Debug and release builds use different keys — update
assetlinks.json accordingly or verification silently fails.
Test verification status with:
adb shell pm get-app-links com.example.myapp
Look for verified status, not just ask.
iOS
⚠️ Universal Links do NOT work in the Simulator. You must test on a
physical device.
AASA changes take up to 24 hours to propagate through Apple's CDN
(iOS 14+). During development, use the ?mode=developer query param or
Apple's CDN diagnostics: swcutil dl -d example.com.
Handle all three entry points: FinishedLaunching, ContinueUserActivity,
and SceneWillConnect. Missing any one causes links to fail for specific
app states (cold start, background resume, or scene-based launch).
applinks: prefix is required in the Associated Domains entitlement.
Writing just example.com instead of applinks:example.com silently fails.
Common Mistakes
Forgetting MainThread.BeginInvokeOnMainThread
Deep link callbacks can fire on background threads. Shell navigation must run
on the main thread.
// ❌ May crash — GoToAsync called off the main thread
static void HandleUniversalLink(string? url)
{
if (string.IsNullOrEmpty(url)) return;
Shell.Current.GoToAsync(MapToRoute(url));
}
// ✅ Dispatch to main thread
static void HandleUniversalLink(string? url)
{
if (string.IsNullOrEmpty(url)) return;
MainThread.BeginInvokeOnMainThread(async () =>
await Shell.Current.GoToAsync(MapToRoute(url)));
}
Route not registered before navigation
Register Shell routes in AppShell constructor before any deep link can
fire. If the route doesn't exist, GoToAsync throws silently or navigates
to root.
Custom URI schemes vs. App Links / Universal Links
Approach
Verified
Fallback to browser
Recommended
Custom URI scheme (myapp://)
No
No
Only for app-to-app communication
Android App Links (https://)
Yes
Yes
✅ Production web links
iOS Universal Links (https://)
Yes
Yes
✅ Production web links
⚠️ Custom URI schemes are not verified — any app can register the same
scheme. Use https:// App Links / Universal Links for user-facing URLs.
Debugging Checklist
Android: IntentFilter has AutoVerify = true on MainActivity
Android: assetlinks.json at /.well-known/ with correct SHA-256 for current signing key
Android: Intent handled in both OnCreate and OnNewIntent
Android: Verified with adb shell pm get-app-links
iOS: applinks:example.com in Associated Domains entitlement (not just example.com)
iOS: AASA file at /.well-known/apple-app-site-association with correct Team ID
iOS: All three lifecycle entry points handled
iOS: Tested on physical device (not simulator)
Shell routes registered before deep link callbacks fire
Navigation dispatched to main thread
Converted and distributed by TomeVault — claim your Tome and manage your conversions.
1---2name: davidortinau-maui-skills-maui-deep-linking3description: .NET MAUI Deep Linking4---56# .NET MAUI Deep Linking78## Platform Gotchas910### Android1112- **`AutoVerify = true` is required** on the `IntentFilter` for App Links (not13 just deep links). Without it, Android shows a disambiguation dialog instead14 of opening your app directly.15- **Handle intent in both `OnCreate` and `OnNewIntent`.**16 `OnCreate` fires for cold starts; `OnNewIntent` fires when the app is already17 running. Missing either means links silently fail in one scenario.1819```csharp20// ❌ Only handles cold-start links21protected override void OnCreate(Bundle? savedInstanceState)22{23 base.OnCreate(savedInstanceState);24 HandleDeepLink(Intent);25}2627// ✅ Handles both cold-start and warm-start links28protected override void OnCreate(Bundle? savedInstanceState)29{30 base.OnCreate(savedInstanceState);31 HandleDeepLink(Intent);32}33protected override void OnNewIntent(Intent? intent)34{35 base.OnNewIntent(intent);36 HandleDeepLink(intent);37}38```3940- **SHA-256 fingerprint must match the signing key** used for the build you're41 testing. Debug and release builds use different keys — update42 `assetlinks.json` accordingly or verification silently fails.43- **Test verification status** with:44 ```bash45 adb shell pm get-app-links com.example.myapp46 ```47 Look for `verified` status, not just `ask`.4849### iOS5051- ⚠️ **Universal Links do NOT work in the Simulator.** You must test on a52 physical device.53- **AASA changes take up to 24 hours** to propagate through Apple's CDN54 (iOS 14+). During development, use the `?mode=developer` query param or55 Apple's CDN diagnostics: `swcutil dl -d example.com`.56- **Handle all three entry points**: `FinishedLaunching`, `ContinueUserActivity`,57 and `SceneWillConnect`. Missing any one causes links to fail for specific58 app states (cold start, background resume, or scene-based launch).59- **`applinks:` prefix is required** in the Associated Domains entitlement.60 Writing just `example.com` instead of `applinks:example.com` silently fails.6162---6364## Common Mistakes6566### Forgetting `MainThread.BeginInvokeOnMainThread`6768Deep link callbacks can fire on background threads. Shell navigation must run69on the main thread.7071```csharp72// ❌ May crash — GoToAsync called off the main thread73static void HandleUniversalLink(string? url)74{75 if (string.IsNullOrEmpty(url)) return;76 Shell.Current.GoToAsync(MapToRoute(url));77}7879// ✅ Dispatch to main thread80static void HandleUniversalLink(string? url)81{82 if (string.IsNullOrEmpty(url)) return;83 MainThread.BeginInvokeOnMainThread(async () =>84 await Shell.Current.GoToAsync(MapToRoute(url)));85}86```8788### Route not registered before navigation8990Register Shell routes in `AppShell` constructor **before** any deep link can91fire. If the route doesn't exist, `GoToAsync` throws silently or navigates92to root.9394### Custom URI schemes vs. App Links / Universal Links9596| Approach | Verified | Fallback to browser | Recommended |97|---|---|---|---|98| Custom URI scheme (`myapp://`) | No | No | Only for app-to-app communication |99| Android App Links (`https://`) | Yes | Yes | ✅ Production web links |100| iOS Universal Links (`https://`) | Yes | Yes | ✅ Production web links |101102> ⚠️ Custom URI schemes are **not verified** — any app can register the same103> scheme. Use `https://` App Links / Universal Links for user-facing URLs.104105---106107## Debugging Checklist108109- [ ] Android: `IntentFilter` has `AutoVerify = true` on `MainActivity`110- [ ] Android: `assetlinks.json` at `/.well-known/` with correct SHA-256 for current signing key111- [ ] Android: Intent handled in both `OnCreate` and `OnNewIntent`112- [ ] Android: Verified with `adb shell pm get-app-links`113- [ ] iOS: `applinks:example.com` in Associated Domains entitlement (not just `example.com`)114- [ ] iOS: AASA file at `/.well-known/apple-app-site-association` with correct Team ID115- [ ] iOS: All three lifecycle entry points handled116- [ ] iOS: Tested on **physical device** (not simulator)117- [ ] Shell routes registered before deep link callbacks fire118- [ ] Navigation dispatched to main thread119120---121> Converted and distributed by [TomeVault](https://tomevault.io/claim/davidortinau) — claim your Tome and manage your conversions.122<!-- tomevault:4.0:skill_md:2026-04-11 -->
Run npx skillmds@latest add tomevault-io/davidortinau-maui-skills-maui-deep-linking in your terminal (requires Node.js), paste this page's agent-chat prompt into Claude, Cursor, or any MCP-connected agent, or download the SKILL.md file and copy it into your agent's skills directory.
.NET MAUI Deep Linking It is listed under Coding & Dev Tools on SkillMD.
This skill has not completed SkillMD's automated safety review yet. Independent scanners report: SkillSpector: PASS, Skill Scanner: PASS. SkillMD never runs a skill's scripts for you; review the SKILL.md before installing.
This skill is tagged as working with Claude Code, Claude.ai, OpenAI Codex. SKILL.md is an open format, so most agents that read a skills directory can load it too.
Yes. Installing skills from SkillMD is free, and the skill stays under its author's original license.
tomevault-io (@tomevault-io) published this skill. Their other Agent Skills are listed on their SkillMD profile.