同一家公司或者同一个集团开发的Android应用可以把数据存储在公共目录下互相访问,而iOS因为它独有的沙盒(Sandbox)机制,应用间是不可以互相访问的。iOS应用之间的通讯,可以使用universal links,它同样可以提供iOS应用和网页的通讯。
Apps & Websites 通讯
更多信息可以参考https://developer.apple.com/documentation/uikit/inter-process_communication/allowing_apps_and_websites_to_link_to_your_content?changes=latest_minor
如果你设置了相关的跳转绑定,当你点击特定链接时,系统会为你打开对应的app,如果该app没有安装,则会按链接内容定向到原来的网页。
首先在项目中配置关联的链接:
官方说明https://help.apple.com/xcode/mac/current/#/dev55e137b57
点击项目配置中的当前项目Target,选择Signing & Capabilities,选择All,+Capability,选择Associated Domains。(注意先登录开发者账号,账号绑定的developer配置中才能管理这个权限,在这里才能显示这个配置,否则选择不了)
添加的域名需要按以下格式:
<service>:<fully qualified domain>[:port number]
如果需要匹配所有子域名可以使用通配符*.
这样配置后你的应用就可以被对应域名的web应用访问了。
Server 配置
而怎么可以保证你的网页被你的应用调用同时不会被其他应用拦截,可以在服务器配置apple-app-site-association。
更多信息可以参考https://developer.apple.com/documentation/security/password_autofill/setting_up_an_app_s_associated_domains
具体做法是创建一个json文件配置以下内容,把需要交互的应用包名填进去
格式是 < Team Identifier>.< Bundle Identifier>
{
"webcredentials": {
"apps": [ "D3KQX62K1A.com.example.DemoApp",
"D3KQX62K1A.com.example.DemoAdminApp" ]
}
}
放到服务器的下列路径
1.iOS9.3之前是根目录下
https:///apple-app-site-association
2.iOS9.3之后是well-known目录
https:///.well-known/apple-app-site-association
配置文件有以下要求
Note
In iOS 9.3.1 and later, the file must be no larger than 128 KB (uncompressed). If your app runs in iOS 8, the file must have the MIME type application/pkcs7-mime and it must be CMS signed by a valid TLS certificate.
如果发生以下情况,验证可能会失败,并且关联将被拒绝:
JSON文件无效或不包含应用程序标识符。
服务器返回300-499码。 这包括重定向。
如果服务器返回500-599码,则系统认为该文件暂时不可用,然后重试。 默认情况下,系统每三小时重试一次,最多八次。
应用程序成功与域关联后,它将保持关联状态,直到从设备中删除该应用程序为止。 在开发期间,每次更新关联文件以立即查看更改时,从测试设备中删除您的应用程序。
Apps 之间通讯
就像文章开始说的,实现iOS沙盒间数据交互需要用到universal links的方式。
应用可以通过UIApplication的open(_:options:completionHandler:)方法唤醒其他应用,前提是其他应用设置了对应的域名让iOS系统匹配成功。你可以在url中以query string的形式拼接参数代表模拟交互的功能。下面是一个universal link的例子:
if let appURL = URL(string: "https://myphotoapp.example.com/albums?albumname=vacation&index=1") {
UIApplication.shared.open(appURL) { success in
if success {
print("The URL was delivered successfully.")
} else {
print("The URL failed to open.")
}
}
} else {
print("Invalid URL specified.")
}
值得注意的是,该方法适用于应用间交互,如果你想用这个方法跳转到html页面,是不会有反应的。
让Universal link生效
1.配置app项目工程的Associated Domains,在前面已经有了。
https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_developer_associated-domains?changes=latest_minor
2.配置服务器的Associated Domains Entitlement,在上面也有了
https://developer.apple.com/documentation/security/password_autofill/setting_up_an_app_s_associated_domains?changes=latest_minor#3001207
3.配置应用端的Apple App Site Association file,跟服务端的匹配
查看下面[在App端对服务端配置进行支持]
4.Website和App交互的数据安全保障可以通过iCloudKeychain和credentials API保证,查看具体流程
https://developer.apple.com/documentation/security/shared_web_credentials?changes=latest_minor
5.处理App端Universal link的响应
https://developer.apple.com/documentation/uikit/inter-process_communication/allowing_apps_and_websites_to_link_to_your_content/handling_universal_links?changes=latest_minor
在App端对服务端配置进行支持
工程添加一个配置文件Apple App Site Association file:
每一个app对应一个字典;
配置格式是团队id和包id ..;
路径跟服务端的互相匹配(区分大小写)
{
"applinks": {
"apps": [],
"details": [{
"appID": "D3KQX62K1A.com.example.photoapp",
"paths": ["/albums"]
},
{
"appID": "D3KQX62K1A.com.example.videoapp",
"paths": ["/videos"]
}]
}
}
对于路径的配置可以使用通配符,如果多个路径都匹配同一个url,则先匹配的会生效。你可以用一个app处理一个url的模块,用另一个app处理同一个网站的url的另一个模块。同时你可以使用NOT标签标记不匹配的url。
"paths": [
"/videos/collections/*",
"NOT /videos/samples/2010/*",
"/videos/samples/201?/*"
]
响应和处理Universal link
当universal link生效时,iOS会启动app并发送NSUserActivity对象,你可以检查这个对象的属性判断当前的启动方式和进行后续的用户交互。
让universal link生效必须先在服务端和app端配置关联文件,见上方;在app delegate中添加响应该对象的代理方法
在app delegate中提供以下方法:
func application(_ application: UIApplication,
continue userActivity: NSUserActivity,
restorationHandler: @escaping ([Any]?) -> Void) -> Bool
1.当iOS系统通过universal link打开app时,NSUserActivity对象的activityType是NSUserActivityTypeBrowsingWeb
2.NSUserActivity的webpageURL属性包含用户访问的HTTP或HTTPS URL
3.使用NSURLComponents API提取URL的组件
func application(_ application: UIApplication,
continue userActivity: NSUserActivity,
restorationHandler: @escaping ([Any]?) -> Void) -> Bool
{
guard userActivity.activityType == NSUserActivityTypeBrowsingWeb,
let incomingURL = userActivity.webpageURL,
let components = NSURLComponents(url: incomingURL, resolvingAgainstBaseURL: true),
let path = components.path,
let params = components.queryItems else {
return false
}
print("path = \(path)")
if let albumName = params.first(where: { $0.name == "albumname" } )?.value,
let photoIndex = params.first(where: { $0.name == "index" })?.value {
print("album = \(albumName)")
print("photoIndex = \(photoIndex)")
return true
} else {
print("Either album name or photo index missing")
return false
}
}
Universal link提供了良好的native和html交互体验同时会带来安全隐患,编写程序的时候需要注意数据安全,不在该交互链中提供用户敏感信息和进行敏感的用户操作,并考虑防止多余数据的插入,对不符合自定义格式的数据进行筛选。
提供一些相关的学习链接
附带视频学习链接:
H5打开APP技术总结——https://blog.youkuaiyun.com/zy1281539626/article/details/79203853
Seamless Linking to Your App——https://developer.apple.com/videos/play/wwdc2015/509/