Xamarin and .NET MAUI apps share C# business logic across platforms but require platform-specific configuration for deep linking. The verification files (AASA, assetlinks.json), entitlements, and Intent filters are native concerns. The URL routing logic can be shared.
For Kotlin Multiplatform deep linking, see Kotlin Multiplatform deep links. For the cross-platform overview, see cross-platform deep linking guide for 2026.
.NET MAUI Setup (Recommended)
.NET MAUI is the successor to Xamarin.Forms. If you are starting a new project, use MAUI.
iOS Configuration
1. Add Associated Domains
In your MAUI project, add the entitlement in Platforms/iOS/Entitlements.plist:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.developer.associated-domains</key>
<array>
<string>applinks:yourdomain.com</string>
</array>
</dict>
</plist>
2. Handle Universal Links in AppDelegate
In Platforms/iOS/AppDelegate.cs:
[Export("application:continueUserActivity:restorationHandler:")]
public bool ContinueUserActivity(
UIApplication application,
NSUserActivity userActivity,
UIApplicationRestorationHandler completionHandler)
{
if (userActivity.ActivityType == NSUserActivityType.BrowsingWeb
&& userActivity.WebPageUrl != null)
{
var url = userActivity.WebPageUrl.ToString();
DeepLinkHandler.HandleUrl(url);
return true;
}
return false;
}
Android Configuration
1. Add Intent Filters
In Platforms/Android/AndroidManifest.xml:
<activity android:name=".MainActivity" android:exported="true">
<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="yourdomain.com"
android:pathPrefix="/products" />
<data android:scheme="https"
android:host="yourdomain.com"
android:pathPrefix="/offers" />
</intent-filter>
</activity>
Or use the IntentFilter attribute on your MainActivity:
[Activity(Theme = "@style/Maui.SplashTheme", MainLauncher = true, Exported = true)]
[IntentFilter(
new[] { Intent.ActionView },
Categories = new[] { Intent.CategoryDefault, Intent.CategoryBrowsable },
DataScheme = "https",
DataHost = "yourdomain.com",
DataPathPrefix = "/products",
AutoVerify = true)]
public class MainActivity : MauiAppCompatActivity
{
protected override void OnNewIntent(Intent intent)
{
base.OnNewIntent(intent);
if (intent?.Data != null)
{
DeepLinkHandler.HandleUrl(intent.Data.ToString());
}
}
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
if (Intent?.Data != null)
{
DeepLinkHandler.HandleUrl(Intent.Data.ToString());
}
}
}
Shared Routing Logic
Deep Link Router (Shared Code)
Create a shared deep link handler that works on both platforms:
// DeepLinkHandler.cs (shared project)
public static class DeepLinkHandler
{
public static void HandleUrl(string urlString)
{
if (!Uri.TryCreate(urlString, UriKind.Absolute, out var uri))
return;
var path = uri.AbsolutePath;
var query = System.Web.HttpUtility.ParseQueryString(uri.Query);
// Match routes
if (TryMatch(path, "/products/{0}", out var productId))
{
NavigateToProduct(productId, query);
return;
}
if (TryMatch(path, "/offers/{0}", out var offerId))
{
NavigateToOffer(offerId, query);
return;
}
if (TryMatch(path, "/referral/{0}", out var referrerId))
{
NavigateToReferral(referrerId, query);
return;
}
// Default
NavigateToHome();
}
private static bool TryMatch(string path, string pattern, out string param)
{
param = null;
var prefix = pattern.Replace("{0}", "");
if (!path.StartsWith(prefix)) return false;
param = path.Substring(prefix.Length).TrimEnd('/');
return !string.IsNullOrEmpty(param);
}
private static async void NavigateToProduct(string productId, NameValueCollection query)
{
await Shell.Current.GoToAsync($"//products/{productId}");
}
private static async void NavigateToOffer(string offerId, NameValueCollection query)
{
await Shell.Current.GoToAsync($"//offers/{offerId}");
}
private static async void NavigateToReferral(string referrerId, NameValueCollection query)
{
await Shell.Current.GoToAsync($"//referral/{referrerId}");
}
private static async void NavigateToHome()
{
await Shell.Current.GoToAsync("//home");
}
}
With Shell Navigation
.NET MAUI Shell provides built-in route registration:
// AppShell.xaml.cs
public partial class AppShell : Shell
{
public AppShell()
{
InitializeComponent();
// Register routes for deep linking
Routing.RegisterRoute("products", typeof(ProductPage));
Routing.RegisterRoute("offers", typeof(OfferPage));
Routing.RegisterRoute("referral", typeof(ReferralPage));
}
}
Query Parameter Handling
// ProductPage.xaml.cs
[QueryProperty(nameof(ProductId), "productId")]
[QueryProperty(nameof(Source), "source")]
public partial class ProductPage : ContentPage
{
public string ProductId { get; set; }
public string Source { get; set; }
protected override void OnAppearing()
{
base.OnAppearing();
LoadProduct(ProductId);
// Track attribution
if (!string.IsNullOrEmpty(Source))
{
Analytics.TrackDeepLink(Source, $"/products/{ProductId}");
}
}
}
Xamarin.Forms (Legacy)
If you are still on Xamarin.Forms (end of support: May 2024, extended support varies), the approach is similar but uses Xamarin-specific APIs.
iOS (Xamarin.iOS)
// AppDelegate.cs
public override bool ContinueUserActivity(
UIApplication application,
NSUserActivity userActivity,
UIApplicationRestorationHandler completionHandler)
{
if (userActivity.WebPageUrl != null)
{
DeepLinkHandler.HandleUrl(userActivity.WebPageUrl.ToString());
return true;
}
return false;
}
Android (Xamarin.Android)
[Activity(MainLauncher = true)]
[IntentFilter(new[] { Intent.ActionView },
Categories = new[] { Intent.CategoryDefault, Intent.CategoryBrowsable },
DataScheme = "https",
DataHost = "yourdomain.com",
AutoVerify = true)]
public class MainActivity : FormsAppCompatActivity
{
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
if (Intent?.Data != null)
{
DeepLinkHandler.HandleUrl(Intent.Data.ToString());
}
}
}
Testing
iOS Simulator
xcrun simctl openurl booted "https://yourdomain.com/products/abc123"
Android Emulator
adb shell am start -a android.intent.action.VIEW \
-d "https://yourdomain.com/products/abc123" \
-c android.intent.category.BROWSABLE
Unit Testing the Router
[Test]
public void HandleUrl_ProductLink_NavigatesToProduct()
{
var url = "https://yourdomain.com/products/abc123?ref=email";
// Test that the routing logic correctly parses the URL
var result = DeepLinkRouter.Parse(url);
Assert.AreEqual("ProductPage", result.Screen);
Assert.AreEqual("abc123", result.Params["productId"]);
Assert.AreEqual("email", result.Query["ref"]);
}
Tolinku for .NET Apps
Tolinku hosts AASA and assetlinks.json files, handles deferred deep links, and provides analytics. See the deep linking documentation for integration details.
For implementation from scratch, see how to implement deep links from scratch. For the full guide, see cross-platform deep linking guide for 2026.
Get deep linking tips in your inbox
One email per week. No spam.