Hlboké odkazy v .NET MAUI: App Links a Universal Links pre iOS a Android (2026)
Praktický návod, ako v .NET MAUI 10 spojazdniť App Links na Androide a Universal Links na iOS — od overovacích súborov cez Shell routing až po lokálne testovanie.
Hlboké odkazy v .NET MAUI implementujete pomocou kombinácie platformových mechanizmov (App Links na Androide a Universal Links na iOS), ktoré nasmerujete do Shell routingu cez metódu Shell.Current.GoToAsync. Vďaka tomu jedna HTTPS URL otvorí vašu aplikáciu natívne na obidvoch platformách a v prípade neprítomnosti aplikácie sa elegantne otvorí v prehliadači. V tomto sprievodcovi prejdeme kompletnú produkčnú konfiguráciu, vrátane súborov assetlinks.json a apple-app-site-association, ktoré reálne fungujú v .NET MAUI 10.
App Links (Android) a Universal Links (iOS) sú dve platformovo špecifické technológie, ktoré v .NET MAUI 10 musíte konfigurovať oddelene, hoci zdieľajú spoločnú HTTPS doménu.
Vlastné URI schémy (myapp://) sú stále podporované, no Apple aj Google odporúčajú HTTPS odkazy s overením domény pre vyššiu dôveru a SEO atribúciu.
Na Androide potrebujete súbor /.well-known/assetlinks.json s SHA-256 odtlačkom podpisového certifikátu a atribút android:autoVerify="true" v manifeste.
Na iOS potrebujete súbor /.well-known/apple-app-site-association a oprávnenie com.apple.developer.associated-domains v entitlements.
Smerovanie do konkrétnej stránky riešite cez Shell.Current.GoToAsync s parametrami, ideálne v App.xaml.cs alebo v platformových MainActivity a AppDelegate.
Otestovať odkazy lokálne dokážete cez adb shell am start a xcrun simctl openurl bez nutnosti publikovať aplikáciu do obchodu.
Čo sú hlboké odkazy a prečo na nich záleží
Hlboký odkaz (deep link) je URL, ktorá z webu, e-mailu alebo notifikácie otvorí konkrétnu obrazovku v natívnej aplikácii namiesto prehliadača. V mobilnom marketingu sú kľúčové pre používateľskú konverziu. Z reklamy v Google Ads alebo Meta Ads vás presmerujú priamo na produkt, nie na home screen, kde sa väčšina používateľov stratí. Pre vývojárov sú zase zásadným nástrojom pre OAuth callback flow, magic linky pri prihlasovaní a integráciu s push notifikáciami.
V .NET MAUI máte tri možnosti, ako hlboké odkazy implementovať. Prvá sú vlastné URI schémy ako myapp://product/42. Fungujú rýchlo a jednoducho, ale ktokoľvek si môže registrovať rovnakú schému a unesie tak vašu navigáciu. Druhá je App Links na Androide, kde HTTPS doména musí overiť svoj vzťah k aplikácii. Tretia je Universal Links na iOS s rovnakým princípom overenia. V produkcii dnes potrebujete kombináciu všetkých troch: vlastné schémy pre OAuth a HTTPS odkazy pre marketing.
Som rád, že MAUI 10 už nemá rovnaké slepé miesta ako prvé verzie. Pamätám si, ako sme v jednej app v roku 2023 strávili dva dni odlaďovaním toho, prečo Shell nevedel rozparsovať URL parametre z deep linku. Bolo to tým, že Shell.Current bol v okamihu volania null. Dnes je životný cyklus stabilnejší, ale rozdiely medzi platformami zostávajú a v tomto článku ich rozoberieme do hĺbky.
Aký je rozdiel medzi deep links a universal links?
Termín "deep link" je zastrešujúci pojem pre akúkoľvek URL otvárajúcu konkrétnu obrazovku v aplikácii. Universal Links a App Links sú špecifické implementácie tohto konceptu, ktoré navyše požadujú kryptografické overenie, že doména skutočne patrí majiteľovi aplikácie. Vďaka tomu nemôže iná aplikácia "ukradnúť" otvorenie vašej URL. Operačný systém pred otvorením skontroluje súbor na webe a porovná ho s identifikátorom aplikácie.
Vlastnosť
Vlastná URI schéma
Android App Links
iOS Universal Links
Formát URL
myapp://path
https://domena.sk/path
https://domena.sk/path
Overenie domény
Žiadne
assetlinks.json
apple-app-site-association
Fallback v prehliadači
Nie (chyba)
Áno, automatický
Áno, automatický
Možnosť únosu inou app
Áno
Nie (po overení)
Nie (po overení)
Vyžaduje HTTPS server
Nie
Áno (TLS, valid cert)
Áno (TLS, valid cert)
Najlepšie pre
OAuth callback, IPC
Marketing, e-maily
Marketing, e-maily
Praktické pravidlo, ktoré aplikujem: pre marketing, e-maily a obnovu hesla použite HTTPS App/Universal Links. Pre OAuth, prepínanie medzi vlastnými aplikáciami a interné mechanizmy ponechajte vlastnú schému. Detailnejší pohľad na bezpečnú výmenu tokenov nájdete v článku autentifikácia v .NET MAUI s OAuth2 a JWT, kde callback pomocou vlastnej schémy hrá kľúčovú rolu.
Ako nastaviť App Links na Androide
App Links na Androide vyžadujú tri kroky: úprava manifestu, nasadenie assetlinks.json na váš HTTPS server a obsluha intentu v MainActivity. Začnime manifestom. V .NET MAUI 10 sa konfigurácia robí cez atribúty v Platforms/Android/MainActivity.cs, čo je čistejšie než ručná úprava XML.
Druhý krok je súbor assetlinks.json, ktorý musí byť dostupný na presnej URL https://mobiletechlead.com/.well-known/assetlinks.json s MIME typom application/json a bez presmerovaní. SHA-256 odtlačok získate cez keytool -list -v -keystore release.keystore alebo z Play Console v sekcii App signing.
Tretí krok je samotná verifikácia. Po inštalácii aplikácie alebo aktualizácii spustí Android automaticky overenie na pozadí. Stav skontrolujete príkazom adb shell pm get-app-links com.mobiletechlead.app, ktorý vráti verified alebo none. Ak vidíte none, najčastejšou príčinou je nesprávny SHA-256 alebo neprístupný .well-known endpoint (napr. ho blokuje Cloudflare Worker alebo CDN cache).
Ako nastaviť Universal Links na iOS
Universal Links na iOS sú architektonicky podobné, ale konfiguračne odlišné. Potrebujete tri komponenty: oprávnenie com.apple.developer.associated-domains v Entitlements.plist, súbor apple-app-site-association na webe a kód v AppDelegate pre prevzatie URL. V .NET MAUI 10 vyzerá entitlements súbor takto:
Pozor, Entitlements.plist musíte priradiť v .csproj cez <CodesignEntitlements>Platforms\iOS\Entitlements.plist</CodesignEntitlements> pre obidva Debug aj Release buildy, inak Xcode pri archive proces oprávnenie zahodí. Toto je ďalšia z chýb, ktorá ma stála pol dňa pri prvom MAUI projekte.
Súbor apple-app-site-association (bez prípony) musí byť dostupný na https://mobiletechlead.com/.well-known/apple-app-site-association, opäť bez presmerovania, s MIME typom application/json. Identifikátor je vo formáte TEAM_ID.BUNDLE_ID. Team ID nájdete v Apple Developer portáli.
V AppDelegate.cs pridáme prepísanie metódy ContinueUserActivity, ktorá sa zavolá pri otvorení Universal Linku:
[Register("AppDelegate")]
public class AppDelegate : MauiUIApplicationDelegate
{
protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();
public override bool ContinueUserActivity(UIApplication application,
NSUserActivity userActivity,
UIApplicationRestorationHandler completionHandler)
{
if (userActivity.ActivityType == NSUserActivityType.BrowsingWeb
&& userActivity.WebPageUrl is { } url)
{
MainThread.BeginInvokeOnMainThread(async () =>
{
await DeepLinkRouter.RouteAsync(url.AbsoluteString);
});
return true;
}
return base.ContinueUserActivity(application, userActivity, completionHandler);
}
}
Detailný popis JSON formátu a pokročilé komponenty (regex, query params) nájdete v oficiálnej Apple dokumentácii k associated domains. Stojí za prečítanie, najmä sekcia o sprevádzajúcich query parametroch (tam je veľa podporovaných možností, ktoré málokto využíva).
Smerovanie do Shell stránok s parametrami
Keď máme platformové vrstvy vyriešené, prichádza zaujímavá zdieľaná časť, a tou je smerovanie URL na konkrétnu MAUI stránku. Odporúčam to centralizovať do jednej triedy, ktorú zavoláme z oboch platforiem. Tým vyhneme duplicite a získame jediné miesto na úpravy.
public static class DeepLinkRouter
{
public static async Task RouteAsync(string url)
{
if (string.IsNullOrWhiteSpace(url)) return;
var uri = new Uri(url);
var path = uri.AbsolutePath.Trim('/').Split('/');
if (path.Length == 0) return;
// Počkáme, kým je Shell pripravený (pri studenom štarte
// môže byť volaný pred dokončením inicializácie).
await WaitForShellAsync();
var query = HttpUtility.ParseQueryString(uri.Query);
switch (path[0])
{
case "app" when path.Length >= 3 && path[1] == "product":
await Shell.Current.GoToAsync(
$"//product?id={path[2]}&ref={query["ref"] ?? "deeplink"}");
break;
case "auth" when path.Length >= 2 && path[1] == "callback":
await Shell.Current.GoToAsync(
$"//login/oauth?code={query["code"]}&state={query["state"]}");
break;
default:
await Shell.Current.GoToAsync("//home");
break;
}
}
static async Task WaitForShellAsync(int timeoutMs = 3000)
{
var start = Environment.TickCount;
while (Shell.Current is null
&& Environment.TickCount - start < timeoutMs)
{
await Task.Delay(50);
}
}
}
Tento prístup som už nasadil v troch produkčných aplikáciách a má jednu kritickú vlastnosť: čaká, kým Shell.Current nie je null. Pri studenom štarte z deep linku sa totiž môže stať, že OnCreate v MainActivity beží skôr ako MAUI Shell stihne inicializovať Window. Bez čakania dostanete NullReferenceException a navigácia tichá zlyhá.
Cieľová stránka prijíma parametre cez atribút QueryProperty, čo je idiomatický spôsob v Shell:
Kombinácia QueryProperty a CommunityToolkit.Mvvm generátorov je v MAUI 10 stále najčistejší spôsob. Ak ste tento vzor ešte nepoužívali, pozrite si nášho sprievodcu MVVM architektúru v .NET MAUI s CommunityToolkit, kde generátory rozoberáme detailnejšie.
Ako otestovať hlboké odkazy lokálne
Testovanie hlbokých odkazov je často podceňované, ale ušetrí vám hodiny pri reálnom uvedení. Obidve platformy poskytujú CLI nástroje, ktoré simulujú otvorenie URL bez nutnosti prejsť cez Safari alebo Chrome. Na Androide používame adb:
# Test App Linku cez emulátor alebo pripojené zariadenie
adb shell am start -a android.intent.action.VIEW \
-c android.intent.category.BROWSABLE \
-d "https://mobiletechlead.com/app/product/42?ref=email" \
com.mobiletechlead.app
# Overenie stavu autoVerify
adb shell pm get-app-links com.mobiletechlead.app
# Manuálne spustenie verifikácie (užitočné po zmene assetlinks.json)
adb shell pm verify-app-links --re-verify com.mobiletechlead.app
Na iOS simulátore používame xcrun:
# Otvorenie Universal Linku v simulátore
xcrun simctl openurl booted "https://mobiletechlead.com/app/product/42"
# Pre Universal Linky musí byť aplikácia
# v cache zaregistrovaná aspoň raz cez nainštalovanie z Xcode
Pre integračné testovanie odporúčam vytvoriť jednoduchý skript v CI, ktorý po každom builde spustí emulátor, nainštaluje APK a spustí adb príkaz s URL. Ak chcete ísť ešte ďalej, kombinujte to s Appium pre validáciu, že aplikácia skutočne otvorila správnu stránku. Detaily o CI/CD pipeline nájdete v sprievodcovi CI/CD pre .NET MAUI v GitHub Actions.
Prečo moje hlboké odkazy nefungujú? Bežné príčiny
Za posledných päť rokov som videl rovnaké chyby u tímov znova a znova. Zhrniem ich v zostupnom poradí podľa frekvencie:
Nesprávny SHA-256 odtlačok v assetlinks.json. Použili ste lokálny debug keystore namiesto produkčného. Riešenie: získajte odtlačok z Google Play Console alebo z presne toho istého keystore, ktorým sa podpisuje release APK.
HTTP presmerovanie na /.well-known/ endpointe. Cloudflare alebo Nginx presmeruje http:// na https://, čo Apple aj Google považujú za zlyhanie. Riešenie: zabezpečte, aby HTTPS URL vrátila 200 OK priamo, bez akéhokoľvek 301/302.
Nesprávny MIME typ. Server vracia text/html namiesto application/json. Apple je voľnejší, Android striktný.
Chýbajúci autoVerify="true". Bez neho Android otvorí výberový dialóg "Otvoriť cez", čo nie je deep link, ale obyčajný intent handler.
Race condition pri studenom štarte.Shell.Current je null, preto použite WaitForShellAsync ako v ukážke vyššie.
Cache CDN po zmene assetlinks. Cloudflare cachuje /.well-known/ štandardne na hodiny. Vyčistite cache po každej zmene.
Užitočným nástrojom pre validáciu je Google Digital Asset Links Validator, ktorý overí dostupnosť aj formát súboru z perspektívy Androidu. Pre iOS existuje analogická služba na Apple developer portáli s názvom "App Search API Validation Tool", ktorá overuje associated domains.
Pre úplnosť, oficiálny prístup .NET MAUI k app linkom je dokumentovaný v Microsoft Learn dokumentácii o deep linkingu, no nájdete tam minimum informácií o Universal Links na iOS a takmer nič o autoverify problémoch na Androide. Preto si tento postup uchovajte ako referenciu pre váš tím.
Často kladené otázky
Podporuje .NET MAUI deep linking out-of-the-box?
Áno, .NET MAUI 10 podporuje deep linking cez Shell routing a platformové primitíva (IntentFilter na Androide, ContinueUserActivity na iOS). Avšak konfigurácia overenia domény (assetlinks.json a apple-app-site-association) musí byť stále urobená manuálne, MAUI šablóna ju nevygeneruje.
Ako otestujem hlboké odkazy bez publikovania do obchodu?
Na Androide použite príkaz adb shell am start -a android.intent.action.VIEW -d "https://vasa-domena.sk/cesta" balicek.app. Na iOS simulátore xcrun simctl openurl booted "https://vasa-domena.sk/cesta". Obidva nástroje simulujú reálne kliknutie a aktivujú vašu intent / userActivity logiku.
Čo nahradilo Firebase Dynamic Links po ich ukončení?
Firebase Dynamic Links boli ukončené v auguste 2025. Pre čistú deep linking funkcionalitu používajte natívne App Links a Universal Links. Pre deferred deep linking (atribúcia po inštalácii) a marketingovú analytiku zvoľte Branch, Adjust alebo AppsFlyer. Všetky majú SDK kompatibilné s .NET MAUI.
Môžem použiť rovnakú HTTPS doménu pre App Links aj Universal Links?
Áno, a je to odporúčaný prístup. Súbory assetlinks.json a apple-app-site-association sa nachádzajú v rovnakom /.well-known/ adresári a navzájom si neprekážajú. Jedna URL ako https://mojaapp.sk/app/produkt/42 potom funguje na obidvoch platformách.
Ako si poradiť, keď používateľ nemá nainštalovanú aplikáciu?
App Links aj Universal Links majú automatický fallback: keď aplikácia nie je nainštalovaná, systém otvorí URL v predvolenom prehliadači. Preto by váš web mal mať na danej URL aj zmysluplnú HTML stránku, ideálne s tlačidlom "Otvoriť v aplikácii" alebo presmerovaním do App Store / Google Play.
Hot Reload v .NET MAUI 10 prestal fungovať? Prejdite si presné príčiny rude edits, iOS interpreter setup, multi-target footguny a šesťkrokový diagnostický checklist pre rok 2026.
App Center skončil. Praktický návod, ako nasadiť Sentry alebo Firebase Crashlytics v .NET MAUI 2026 - integrácia, upload symbolov, GDPR a check-list pre migráciu.