Skip to content
Tolinku
Tolinku
Sign In Start Free
Engineering · · 4 min read

Xamarin Deep Linking: Cross-Platform Setup

By Tolinku Staff
|
Tolinku webhooks integrations dashboard screenshot for engineering blog posts

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 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

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.

Ready to add deep linking to your app?

Set up Universal Links, App Links, deferred deep linking, and analytics in minutes. Free to start.