概念
通用链接Universal Links
:是HTTP
或HTTPS
的URLs
(统一资源定位符),Apple
的操作系统会将这些URL
识别 为指向网络或App
中的资源。无论是在网上还是在App
中,这意味着无论用户是否下载安装了我们的App
,此URL
都可以表示该内容。
iOS 9.0中引入了通用链接,通用链接在我们的App
和网站之间是安全关联的。App
在Xcode
中采用了一个权限,来指示该App
通用链接涉及哪些域。
我们的WEB
服务器采用了一个JSON
文件,该文件描述了我们App
,也描述了如何过滤域对应的URL
,使得我们期望的URL
可以在App
中打开。这种双向安全握手确保没有人可以将用户重定向到其他的App
而不是我们的App
。
关于使用苹果强烈建议使用通用链接代替之前自定义的URL schemes
。因为自定义的URL schemes
的方式本质上是不安全的,可能被恶意开发人员滥用。
配置
配置WEB
服务器
WEB
服务器必须有一个有效的HTTPS
证书。HTTP不安全,不能用于验证App和网站之间的关联。用于签名HTTPS
的根证书必须被操作系统识别,不支持自定义的根证书。生成证书并配置服务器之后,需要添加Apple-app-site-association
文件,这是一个JSON
文件,文件的格式我们稍后讨论。当我们App
安装在苹果的设备上时,苹果的操作系统将会下载该文件,以确定服务器允许我们的App
使用那些服务。系统还会定期下载此文件的更新。通用链接是这个文件中可能包含的众多服务之一。
通用链接示例:https://example.com/.well-known/Apple-app-site-association
Apple-app-site-association
这个文件应该位于我们的域名example.com
,/.well-known
下并且苹果不推荐使用其他路径。
过去,苹果讨论过对Apple-app-site-association
这个文件是否签名,觉得签名从来不是支持普遍联系的必要步骤,所以被弃用了,因此可以不用对Apple-app-site-association
这个文件签名啦。支持已签名的文件,处在其他路径的JSON
文件将在将来的版本中取消对其的支持。
Apple-app-site-association
文件格式:
顶层字典其KEY
是服务类型,对于通用链接,KEY
为applink
,但是也可以使用其他服务的关键字。聚焦于通用链接,在applink
下面的KEY
为apps
,details
。
apps
:如果是在iOS 13
上我们是不需要apps
的,所以可以删除。如果对于iOS 13
以前的版本需要继续提供支持,则仍然需要保留apps
。对于通用链接来说apps
应该总是一个空数组。
details
:details
键对应的值是一个包含字典类型的数组,每个字典代表一个特定App
的通用链接配置。过去此处我们支持使用字典结构,而不是数组结构,但是这种配置方式已经过时了。
details
键下面的appID
对应的值是我们的App
标识符。我们的App
标识符由Apple
提供的字母数字(10个字符)前缀和App
的bundleId
组成。前缀可能等于也可能不等于我们的团队标识符,检查Apple developer
门户网站确认App
标识符。如果我们的App
具有多个相同通用链接配置,我们可能不想重复关联JSON
文件,若是iOS 13
,则可以使用appIDs
键,来减少该文件的大小。appIDs
键的值是一个App
标识符数组。
若需要支持以前的版本,则应该继续为每个App
使用单一的appID
键。
details
键下面的paths
键对应的值是路径模式数组。模式匹配与在终端中执行方式相同。*
表示通配符,?
匹配一个字符。
从2019
年也就是iOS 13
开始,苹果用components
键替换了paths
键。components
键的值是一个字典数组,每个字典称为一组URL
的匹配模式,也称为一个组件,每个组件都包含零个或多个匹配URL
不同组成部分语句,来模式匹配URL
。与先前路径模式数组一样组件也可以匹配URL
的路径。在组件中匹配URL
的path
部分的键是"/"
,值(示例)为:/path/*/filename
。如果需要支持以前的版本可以保留paths
键,在iOS 13
中如果components
键存在将忽略paths
键。
新增匹配项:
每个组件新增匹配URL
的Fragment
部分,在组件中匹配URL
的Fragment
部分的键是"#"
,值(示例)为:"*fragment"
。
每个组件新增匹配URL
的Query
部分,在组件中匹配URL
的Query
部分的键是"?"
,值(示例)为:"*widget=?*"
或指定为字典:{"widget":"?*","grommet":"please"}
。
要使组件字典匹配候选的URL
,则组件中的匹配项都必须匹配。如果不指定组件某个匹配项:路径,Fragment等,操作系统默认行为便是忽略匹配URL
对应的那一部分。例如我们的App
不关心URL
的Fragment
则不需要在组件中指定Fragment
的模式匹配项。我们的网站可能有一些部分,还不能在App
中显示,我们可以在组件中通过指定exclude
键对应的的布尔值为true
来排除这些子部分,此组件匹配到的URL
将不会作为通用链接在App
中打开。此键具有与在旧的paths
键中使用Not
关键字相同的行为,在使用组件字典时,不支持Not
关键字。
这里有一些URL
使用组件进行模式匹配的例子:
注意点:
-
URL
必须始终使用ASCII
编码,组件模式匹配也是用ASCII
码完成的。 - 国际化时为减少
Apple-app-site-association
文件的下载大小,可以使用更灵活的匹配方式。
- 操作系统会根据用户最可能浏览的位置(域)对
Apple-app-site-association
文件下载进行优先级排序。虽然在安装App
时都会下载它们,但是优先级不同,顶级域名.com
,.net
和.org
是高优先级的域,因为用户流量大。还有用户的ccTLD
(国家代码TLD),若国际化的TLD与用户当前语言环境匹配,也会被优先下载。
配置App
在Xcode 13
中导航到Signning&Capabilities
添加Associated Domains
功能。
处理通用链接
激活通用链接,iOS将启动App
并且向它发送NSUserActivity
对象。我们需要解析这个对象,确定启动方式,决定如何处理。
在您的应用程序和网站之间创建双向关联,并指定应用程序处理的URL
当
App
收到NSUserActivity
对象并且对象的属性ActivityType
为NSUserActivityTypeBrowsingWeb
时,及时做出响应。
当iOS通过通用链接打开我们的应用程序时,应用程序会收到一个NSUserActivity
对象,其属性activityType
值为NSUserActivityTypeBrowsingWeb
。对象的webpageURL
属性包含用户访问的HTTP
或HTTPS
URL。使用NSURLComponents
提取URL的组件。
//https://dev-api.yidux.cn/path/wally/test?name=Wally&age=1
func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
guard userActivity.activityType == NSUserActivityTypeBrowsingWeb,
let webpageUrl = userActivity.webpageURL,
let components = NSURLComponents.init(url: webpageUrl, resolvingAgainstBaseURL: true),
let path = components.path
else {
return false
}
print("URL是\(webpageUrl.absoluteString),URL的组件:\(components)")
print("PATH:\(path)")
print("QUERY:\(String(describing: components.queryItems))")
if let queryItems = components.queryItems , let name = queryItems.first(where: {$0.name == "name"})?.value, let age = queryItems.first(where: {$0.name == "age"})?.value {
print("QUERY部分,name = \(name),age = \(age)")
}
return true
}