maui-deep-linking
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
Chinese.NET MAUI Deep Linking
.NET MAUI 深度链接
Use this skill when adding deep link or app link support to a .NET MAUI application.
当你需要为.NET MAUI应用添加深度链接或App Link支持时,请使用本技能。
Android App Links
Android App Links
IntentFilter on MainActivity
在MainActivity上配置IntentFilter
csharp
[IntentFilter(
new[] { Android.Content.Intent.ActionView },
Categories = new[] {
Android.Content.Intent.CategoryDefault,
Android.Content.Intent.CategoryBrowsable
},
DataScheme = "https",
DataHost = "example.com",
DataPathPrefix = "/products",
AutoVerify = true)]
public class MainActivity : MauiAppCompatActivity { }- triggers Android domain verification at install time.
AutoVerify = true - Stack multiple attributes for different paths.
IntentFilter
csharp
[IntentFilter(
new[] { Android.Content.Intent.ActionView },
Categories = new[] {
Android.Content.Intent.CategoryDefault,
Android.Content.Intent.CategoryBrowsable
},
DataScheme = "https",
DataHost = "example.com",
DataPathPrefix = "/products",
AutoVerify = true)]
public class MainActivity : MauiAppCompatActivity { }- 会在应用安装时触发Android域名验证。
AutoVerify = true - 可叠加多个属性以支持不同路径。
IntentFilter
Digital Asset Links (domain verification)
Digital Asset Links(域名验证)
Host on your domain over HTTPS:
/.well-known/assetlinks.jsonjson
[{
"relation": ["delegate_permission/common.handle_all_urls"],
"target": {
"namespace": "android_app",
"package_name": "com.example.myapp",
"sha256_cert_fingerprints": ["AA:BB:CC:..."]
}
}]Get SHA-256:
keytool -list -v -keystore my-release-key.keystore -alias alias_name在你的域名上通过HTTPS托管文件:
/.well-known/assetlinks.jsonjson
[{
"relation": ["delegate_permission/common.handle_all_urls"],
"target": {
"namespace": "android_app",
"package_name": "com.example.myapp",
"sha256_cert_fingerprints": ["AA:BB:CC:..."]
}
}]获取SHA-256指纹:
keytool -list -v -keystore my-release-key.keystore -alias alias_nameHandle incoming intents
处理传入的Intent
csharp
protected override void OnCreate(Bundle? savedInstanceState)
{
base.OnCreate(savedInstanceState);
HandleDeepLink(Intent);
}
protected override void OnNewIntent(Intent? intent)
{
base.OnNewIntent(intent);
HandleDeepLink(intent);
}
void HandleDeepLink(Intent? intent)
{
if (intent?.Action != Intent.ActionView || intent.Data is null) return;
Shell.Current.GoToAsync(MapToRoute(intent.Data.ToString()!));
}csharp
protected override void OnCreate(Bundle? savedInstanceState)
{
base.OnCreate(savedInstanceState);
HandleDeepLink(Intent);
}
protected override void OnNewIntent(Intent? intent)
{
base.OnNewIntent(intent);
HandleDeepLink(intent);
}
void HandleDeepLink(Intent? intent)
{
if (intent?.Action != Intent.ActionView || intent.Data is null) return;
Shell.Current.GoToAsync(MapToRoute(intent.Data.ToString()!));
}Test
测试
bash
adb shell am start -W -a android.intent.action.VIEW \
-d "https://example.com/products/42" com.example.myapp
adb shell pm get-app-links com.example.myappbash
adb shell am start -W -a android.intent.action.VIEW \
-d "https://example.com/products/42" com.example.myapp
adb shell pm get-app-links com.example.myappiOS Universal Links
iOS Universal Links
Associated Domains entitlement
Associated Domains权限配置
In :
Entitlements.plistxml
<key>com.apple.developer.associated-domains</key>
<array>
<string>applinks:example.com</string>
</array>在中:
Entitlements.plistxml
<key>com.apple.developer.associated-domains</key>
<array>
<string>applinks:example.com</string>
</array>Apple App Site Association file
Apple App Site Association文件
Host at (Content-Type: ):
/.well-known/apple-app-site-associationapplication/jsonjson
{
"applinks": {
"details": [{
"appIDs": ["TEAMID.com.example.myapp"],
"components": [{ "/": "/products/*" }]
}]
}
}iOS 14+ fetches AASA via Apple's CDN; changes may take 24 hours to propagate.
在路径托管该文件(Content-Type需设为):
/.well-known/apple-app-site-associationapplication/jsonjson
{
"applinks": {
"details": [{
"appIDs": ["TEAMID.com.example.myapp"],
"components": [{ "/": "/products/*" }]
}]
}
}iOS 14+会通过Apple的CDN获取AASA文件;修改后的内容可能需要24小时才能生效。
Handle Universal Links in MAUI
在MAUI中处理Universal Links
csharp
builder.ConfigureLifecycleEvents(events =>
{
#if IOS || MACCATALYST
events.AddiOS(ios =>
{
ios.FinishedLaunching((app, options) =>
{
var activity = options?[UIKit.UIApplication.LaunchOptionsUniversalLinkKey]
as Foundation.NSUserActivity;
HandleUniversalLink(activity?.WebPageUrl?.ToString());
return true;
});
ios.ContinueUserActivity((app, activity, handler) =>
{
if (activity.ActivityType == Foundation.NSUserActivityType.BrowsingWeb)
HandleUniversalLink(activity.WebPageUrl?.ToString());
return true;
});
ios.SceneWillConnect((scene, session, options) =>
{
var activity = options.UserActivities?
.ToArray<Foundation.NSUserActivity>()
.FirstOrDefault(a =>
a.ActivityType == Foundation.NSUserActivityType.BrowsingWeb);
HandleUniversalLink(activity?.WebPageUrl?.ToString());
});
});
#endif
});
static void HandleUniversalLink(string? url)
{
if (string.IsNullOrEmpty(url)) return;
MainThread.BeginInvokeOnMainThread(async () =>
await Shell.Current.GoToAsync(MapToRoute(url)));
}csharp
builder.ConfigureLifecycleEvents(events =>
{
#if IOS || MACCATALYST
events.AddiOS(ios =>
{
ios.FinishedLaunching((app, options) =>
{
var activity = options?[UIKit.UIApplication.LaunchOptionsUniversalLinkKey]
as Foundation.NSUserActivity;
HandleUniversalLink(activity?.WebPageUrl?.ToString());
return true;
});
ios.ContinueUserActivity((app, activity, handler) =>
{
if (activity.ActivityType == Foundation.NSUserActivityType.BrowsingWeb)
HandleUniversalLink(activity.WebPageUrl?.ToString());
return true;
});
ios.SceneWillConnect((scene, session, options) =>
{
var activity = options.UserActivities?
.ToArray<Foundation.NSUserActivity>()
.FirstOrDefault(a =>
a.ActivityType == Foundation.NSUserActivityType.BrowsingWeb);
HandleUniversalLink(activity?.WebPageUrl?.ToString());
});
});
#endif
});
static void HandleUniversalLink(string? url)
{
if (string.IsNullOrEmpty(url)) return;
MainThread.BeginInvokeOnMainThread(async () =>
await Shell.Current.GoToAsync(MapToRoute(url)));
}Testing
测试
- Must test on a physical device. Simulator does not support Universal Links.
- Verify AASA: on macOS.
swcutil dl -d example.com
- 必须在物理设备上测试。模拟器不支持Universal Links。
- 验证AASA文件:在macOS上执行。
swcutil dl -d example.com
Shell Navigation Integration
Shell导航集成
csharp
// Register in AppShell constructor
Routing.RegisterRoute("products/detail", typeof(ProductDetailPage));
static string MapToRoute(string uri)
{
var segments = new Uri(uri).AbsolutePath.Trim('/').Split('/');
return segments switch
{
["products", var id] => $"products/detail?id={id}",
["settings"] => "settings",
_ => "//"
};
}csharp
// 在AppShell构造函数中注册
Routing.RegisterRoute("products/detail", typeof(ProductDetailPage));
static string MapToRoute(string uri)
{
var segments = new Uri(uri).AbsolutePath.Trim('/').Split('/');
return segments switch
{
["products", var id] => $"products/detail?id={id}",
["settings"] => "settings",
_ => "//"
};
}Checklist
检查清单
- Android: with
IntentFilteronAutoVerify = trueMainActivity - Android: at
assetlinks.jsonwith correct SHA-256/.well-known/ - Android: Handle intent in both and
OnCreateOnNewIntent - iOS: in Associated Domains entitlement
applinks: - iOS: AASA file at
/.well-known/apple-app-site-association - iOS: Handle via ,
FinishedLaunching,ContinueUserActivitySceneWillConnect - iOS: Test on physical device (not simulator)
- Shell routes registered and URI-to-route mapping implemented
- Android:在上配置带
MainActivity的AutoVerify = trueIntentFilter - Android:在路径下托管包含正确SHA-256指纹的
/.well-known/文件assetlinks.json - Android:在和
OnCreate中处理IntentOnNewIntent - iOS:在Associated Domains权限中添加配置
applinks: - iOS:在路径托管AASA文件
/.well-known/apple-app-site-association - iOS:通过、
FinishedLaunching、ContinueUserActivity处理Universal LinksSceneWillConnect - iOS:在物理设备上测试(而非模拟器)
- 已注册Shell路由并实现URI到路由的映射逻辑