Apple 通用链接
通常需要连接网站和移动应用,以便网站上的链接启动移动应用并在移动应用中显示内容。 应用链接(也称为深层链接)是一种技术,它使移动设备能够响应 URL 并在 URL 所表示的移动应用中启动内容。
在 Apple 平台上,深层链接称为通用链接。 当用户点击通用链接时,系统会直接将链接重定向到应用,而无需通过 Safari 或网站进行路由。 这些链接可以基于自定义方案,例如 myappname://
,也可以使用 HTTP 或 HTTPS 方案。 例如,单击食谱网站上的链接将打开与该网站关联的移动应用,然后向用户显示特定的食谱。 未安装应用的用户将被带到网站上的内容。 本文重点介绍使用 HTTPS 方案的通用链接。
.NET MAUI iOS 应用支持通用链接。 这需要在域上托管数字资产链接 JSON 文件,该文件描述与应用的关系。 这使 Apple 能够验证尝试处理 URL 的应用是否拥有 URL 域的所有权,以防止恶意应用截获应用链接。
在 .NET MAUI iOS 或 Mac Catalyst 应用中处理 Apple 通用链接的过程如下所示:
- 在网站上创建和托管关联的域文件。 有关详细信息,请参阅创建和托管关联的域文件。
- 向应用添加关联的域权利。 有关详细信息,请参阅向应用添加关联的域权利。
- 在 Apple 开发者帐户中,将关联的域功能添加到应用的应用 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 上添加权利的详细信息,请参阅权利。
权利是使用 String
的 Array
类型的 com.apple.developer.associated-domains
密钥定义的:
<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 中。
将关联的域功能添加到应用 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。 可以使用 SendOnAppLinkRequestReceived 方法将该 URL 传递给你的 App
类。
注意
如果你不在你的应用中使用场景来支持多窗口,则可以省略场景方法的生命周期处理程序。
在你的 App
类中,替代用于接收和处理 URL 的 OnAppLinkRequestReceived 方法:
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());
}
}
在上面的示例中,OnAppLinkRequestReceived 替代会显示应用链接 URL。 实际上,应用链接应将用户直接转到 URL 表示的内容,而不会出现任何提示、登录或其他中断。 因此,OnAppLinkRequestReceived 替代是从中调用到 URL 所表示内容的导航的位置。
警告
通用链接为应用提供了潜在的攻击途径,因此请确保验证所有 URL 参数并丢弃任何格式不正确的 URL。
有关详细信息,请在 developer.apple.com 参阅在应用中支持通用链接。
测试通用链接
重要
在 iOS 上,应在设备上而不是模拟器上测试通用链接。
若要测试通用链接,请将链接粘贴到“备忘录”应用中,并长按它(在 iOS 上)或按 control 并单击它(在 macOS 上),以发现跟随该链接的选项。 如果已正确配置通用链接,则会出现在应用和 Safari 中打开的选项。 你的选择将设置跟随此域中的通用链接时,你的设备上的默认行为。 若要更改此默认选项,请重复这些步骤并做出其他选择。
注意
在 Safari 中输入 URL 始终不会打开应用。 相反,Safari 将接受此操作作为直接导航。 如果用户直接导航到你的域后位于你的域中,那么你的网站将显示一个横幅以打开你的应用。
在 iOS 上,你可以在开发人员设置中使用关联的域诊断测试来测试通用链接:
- 在“设置”中启用开发人员模式。 有关详细信息,请在 developer.apple.com 参阅在设备上启用开发人员模式。
- 在“设置 > 开发人员”中,滚动到“通用链接”,然后启用“关联的域开发”。
- 打开“诊断”并在 URL 中键入。 然后,你将收到有关链接是否对已安装的应用有效的反馈。
通常,无效的通用链接是 applinks
配置错误的结果。
如需故障排除建议,请在 developer.apple.com 参阅调试通用链接。