通常需要连接网站和移动应用,以便网站上的链接启动移动应用并在移动应用中显示内容。 应用链接(也称为 深层链接)是一种技术,使移动设备能够响应 URL 并在 URL 所表示的移动应用中启动内容。
在 Apple 平台上,深层 链接称为通用链接。 当用户点击通用链接时,系统会直接将链接重定向到应用,而无需通过 Safari 或网站进行路由。 这些链接可以基于自定义方案,例如 myappname://,也可以使用 HTTP 或 HTTPS 方案。 例如,单击食谱网站上的链接将打开与该网站关联的移动应用,然后向用户显示特定的食谱。 未安装您应用的用户将被引导至您网站上的内容。 本文重点介绍使用 HTTPS 方案的通用链接。
.NET MAUI iOS 应用支持通用链接。 这需要托管数字资产链接域上的 JSON 文件,该文件描述与应用的关系。 这使 Apple 能够验证尝试处理 URL 的应用是否拥有 URL 域的所有权,以防止恶意应用截获应用链接。
在 .NET MAUI iOS 或 Mac Catalyst 应用中处理 Apple 通用链接的过程如下所示:
- 在网站上创建和托管关联的域文件。 有关详细信息,请参阅 创建和托管关联的域文件。
- 将关联的域权利添加到应用。 有关详细信息,请参阅 向应用添加关联的域权利。
- 在您的 Apple 开发者账户中,将相关域功能添加到您的应用的 App ID。 有关详细信息,请参阅 将关联的域功能添加到应用 ID。
- 请更新您的应用程序,以便在系统将通用链接路由到您的应用程序时,能够响应系统提供的用户活动对象。 有关详细信息,请参阅 “响应通用链接”。
有关详细信息,请参阅 “允许应用和网站链接到 developer.apple.com 上的内容 。 有关为应用定义自定义 URL 方案的信息,请参阅在 developer.apple.com 为 应用定义自定义 URL 方案 。
创建和托管关联的域文件
若要将网站与应用关联,需要在网站上托管关联的域文件。 关联的域文件是一个 JSON 文件,必须在以下位置托管在域上: https://domain.name/.well-known/apple-app-site-association
以下 JSON 显示典型关联域文件的内容:
{
"activitycontinuation": {
"apps": [ "85HMA3YHJX.com.companyname.myrecipeapp" ]
},
"applinks": {
"apps": [],
"details": [
{
"appID": "85HMA3YHJX.com.companyname.myrecipeapp",
"paths": [ "*", "/*" ]
}
]
}
}
这些 apps 和 appID 密钥应为可在网站上使用的应用指定应用标识符。 这些密钥的值由应用标识符前缀和捆绑标识符组成。
重要
必须使用有效的证书托管 https 关联的域文件,且不进行重定向。
有关详细信息,请参阅 developer.apple.com 上的 支持关联域 。
将关联的域名授权添加到你的应用
在你的域名上托管关联的域文件后,需要向应用添加与域相关的权限。 当用户安装您的应用时,iOS 会尝试下载相关的域名文件并验证权限中的域名。
关联的域授权指定了应用关联的域列表。 此权利应添加到应用中 的 Entitlements.plist 文件中。 有关在 iOS 上添加权利的详细信息,请参阅 权利。 有关在 Mac Catalyst 上添加权利的详细信息,请参阅 权利。
权利是使用com.apple.developer.associated-domains密钥定义的,类型为ArrayString:
<key>com.apple.developer.associated-domains</key>
<array>
<string>applinks:recipe-app.com</string>
</array>
有关此权利的详细信息,请参阅 developer.apple.com 上的 关联域权利 。
或者,可以修改项目文件(.csproj),在<ItemGroup>元素中添加特权。
<ItemGroup Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'ios' Or $([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'maccatalyst'">
<!-- For debugging, use '?mode=developer' for debug to bypass apple's CDN cache -->
<CustomEntitlements
Condition="$(Configuration) == 'Debug'"
Include="com.apple.developer.associated-domains"
Type="StringArray"
Value="applinks:recipe-app.com?mode=developer" />
<!-- Non-debugging, use normal applinks:url value -->
<CustomEntitlements
Condition="$(Configuration) != 'Debug'"
Include="com.apple.developer.associated-domains"
Type="StringArray"
Value="applinks:recipe-app.com" />
</ItemGroup>
在此示例中,将域 applinks:recipe-app.com 替换为正确的值。 确保仅包含所需的子域和顶级域。 不包括路径和查询组件或尾部斜杠(/)。
注释
在 iOS 14+ 和 macOS 11+ 中,应用不再直接向 Web 服务器发送文件请求 apple-app-site-association 。 而是将请求发送到专用于关联域的 Apple 托管内容分发网络(CDN)。
将关联的域功能添加到应用 ID
将关联的域权利添加到应用后,需要将关联的域功能添加到 Apple 开发人员帐户中应用的应用 ID。 这是必需的,因为应用中定义的任何权利也需要作为功能添加到 Apple 开发人员帐户中应用的应用 ID 中。
要将关联域功能添加到您的应用识别码:
在 Web 浏览器中,登录到 Apple 开发人员帐户 并导航到 “证书、ID 和配置文件 ”页。
在 “证书,标识符和配置文件 ”页上,选择“ 标识符 ”选项卡。
在 “标识符 ”页上,选择与应用相对应的应用 ID。
在 “编辑应用 ID 配置 ”页上,启用 “关联域 ”功能,然后选择“ 保存 ”按钮:
在“ 修改应用功能 ”对话框中,选择“ 确认 ”按钮。
更新应用的应用 ID 后,需要生成并下载更新的预配配置文件。
注释
如果以后从应用中删除关联的域权利,则需要在 Apple 开发人员帐户中更新应用 ID 的配置。
响应通用链接
当用户激活通用链接时,iOS 和 Mac Catalyst 启动应用并将其发送到对象 NSUserActivity 。 可以查询此对象以确定应用的启动方式,并确定要执行的操作。 这应在FinishedLaunching和ContinueUserActivity生命周期委托中执行。 在FinishedLaunching应用启动时会调用委托,在ContinueUserActivity应用运行或挂起时也会调用委托。 有关生命周期委托的详细信息,请参阅 平台生命周期事件。
若要响应要调用的 iOS 生命周期委托,请在类的方法MauiProgram中CreateMauiapp对MauiAppBuilder对象调用ConfigureLifecycleEvents该方法。 然后,在 ILifecycleBuilder 对象上,调用方法 AddiOS 并指定 Action 来为所需的委托注册一个处理程序。
using Microsoft.Maui.LifecycleEvents;
using Microsoft.Extensions.Logging;
namespace MyNamespace;
public static class MauiProgram
{
public static MauiApp CreateMauiApp()
{
var builder = MauiApp.CreateBuilder();
builder
.UseMauiApp<App>()
.ConfigureFonts(fonts =>
{
fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
})
.ConfigureLifecycleEvents(lifecycle =>
{
#if IOS || MACCATALYST
lifecycle.AddiOS(ios =>
{
// Universal link delivered to FinishedLaunching after app launch.
ios.FinishedLaunching((app, data) => HandleAppLink(app.UserActivity));
// Universal link delivered to ContinueUserActivity when the app is running or suspended.
ios.ContinueUserActivity((app, userActivity, handler) => HandleAppLink(userActivity));
// Only required if using Scenes for multi-window support.
if (OperatingSystem.IsIOSVersionAtLeast(13) || OperatingSystem.IsMacCatalystVersionAtLeast(13))
{
// Universal link delivered to SceneWillConnect after app launch
ios.SceneWillConnect((scene, sceneSession, sceneConnectionOptions)
=> HandleAppLink(sceneConnectionOptions.UserActivities.ToArray()
.FirstOrDefault(a => a.ActivityType == Foundation.NSUserActivityType.BrowsingWeb)));
// Universal link delivered to SceneContinueUserActivity when the app is running or suspended
ios.SceneContinueUserActivity((scene, userActivity) => HandleAppLink(userActivity));
}
});
#endif
});
#if DEBUG
builder.Logging.AddDebug();
#endif
return builder.Build();
}
#if IOS || MACCATALYST
static bool HandleAppLink(Foundation.NSUserActivity? userActivity)
{
if (userActivity is not null && userActivity.ActivityType == Foundation.NSUserActivityType.BrowsingWeb && userActivity.WebPageUrl is not null)
{
HandleAppLink(userActivity.WebPageUrl.ToString());
return true;
}
return false;
}
#endif
static void HandleAppLink(string url)
{
if (Uri.TryCreate(url, UriKind.RelativeOrAbsolute, out var uri))
App.Current?.SendOnAppLinkRequestReceived(uri);
}
}
当 iOS 通过通用链接打开您的应用时,NSUserActivity 对象将有一个属性,其 ActivityType 值为 BrowsingWeb。 活动对象的 WebPageUrl 属性将包含用户想要访问的 URL。 可以使用该方法将 App URL 传递给类 SendOnAppLinkRequestReceived 。
注释
如果不在应用中使用场景来支持多窗口,则可以省略 Scene 方法的生命周期处理程序。
在App类中,重写OnAppLinkRequestReceived方法以接收和处理 URL。
namespace MyNamespace;
public partial class App : Application
{
...
protected override async void OnAppLinkRequestReceived(Uri uri)
{
base.OnAppLinkRequestReceived(uri);
// Show an alert to test that the app link was received.
await Dispatcher.DispatchAsync(async () =>
{
await Windows[0].Page!.DisplayAlert("App link received", uri.ToString(), "OK");
});
Console.WriteLine("App link: " + uri.ToString());
}
}
namespace MyNamespace;
public partial class App : Application
{
...
protected override async void OnAppLinkRequestReceived(Uri uri)
{
base.OnAppLinkRequestReceived(uri);
// Show an alert to test that the app link was received.
await Dispatcher.DispatchAsync(async () =>
{
await Windows[0].Page!.DisplayAlertAsync("App link received", uri.ToString(), "OK");
});
Console.WriteLine("App link: " + uri.ToString());
}
}
在上面的示例中, OnAppLinkRequestReceived 重写显示应用链接 URL。 实际上,应用链接应将用户直接转到 URL 表示的内容,而不会出现任何提示、登录或其他中断。 因此,OnAppLinkRequestReceived 覆盖是用于调用导航到由 URL 表示的内容的位置。
警告
通用链接为应用提供潜在的攻击途径,因此请确保验证所有 URL 参数并丢弃任何格式不正确的 URL。
有关详细信息,请参阅 developer.apple.com 上的应用中支持通用链接 。
测试通用链接
重要
在 iOS 上,应在设备上而不是模拟器上测试通用链接。
若要测试通用链接,请将链接粘贴到 Notes 应用中,并长按它(在 iOS 上)或按住 control 并单击它(在 macOS 上),以发现你选择关注该链接。 如果已正确配置通用链接,则将出现应用和 Safari 中的打开选项。 选择将在此域中遵循通用链接时,在设备上设置默认行为。 若要更改此默认选项,请重复这些步骤并做出其他选择。
注释
在 Safari 中输入 URL 永远不会打开应用。 相反,Safari 将接受此作作为直接导航。 当用户直接导航到你的域名时,你的网站将显示一个横幅以打开你的应用。
在 iOS 上,可以在开发人员设置中测试与关联的域诊断测试的通用链接:
- 在“设置”中启用开发人员模式。 有关详细信息,请参阅在 developer.apple.com 上的设备上启用开发人员模式 。
- 在“设置开发人员”>中,滚动到通用链接并启用关联域开发。
- 打开 诊断 并在 URL 中键入。 然后,你将收到有关链接是否对已安装的应用有效的反馈。
通常,无效的通用链接是错误配置的结果 applinks 。
有关故障排除建议,请参阅在 developer.apple.com 上 调试通用链接 。