Python实战社群
Java实战社群
长按识别下方二维码,按需求添加
扫码关注添加客服
进Python社群▲
扫码关注添加客服
进Java社群▲
作者: pmst,iOS 开发者
来源公众号丨老司机技术周报(LSJCoding)
WWDC20 10174: https://developer.apple.com/videos/play/wwdc2020/10174/
App Clips 是今年 WWDC20 大会的亮点之一,罗列几个开发者感兴趣的问题:什么是 App Clips;实际应用场景中是如何交互的;构建 App Clips 的有哪些前置条件;如何在已有项目中添加对 App Clips 的支持等一系列问题。
在回答上述几个问题之前,我们先来明确三个概念:
App,即我们开发的应用程序,提供完整服务功能,亦称之为主应用;
App Clip Experience,本质是一个 URL 链接,唯一标识某项体验服务,e.g. 餐饮类应用程序中的点餐服务、预定餐桌服务、付款操作等;
App Clip,能够让用户在未安装你发布的完整应用程序情况下,就能体验其中的部分功能,session 中开篇就提到 “App Clips are an additive feature.” ,即附加功能,这意味着你必须先拥有一个 App 主应用程序,才能继续开发该应用的 App Clip,更多关于 App 主应用程序和 App Clip 的关联关系此处先按下不表,下文展开,此外 App Clips 不难让人联想到 Android Instant apps 、微信小程序以及 PWA,三者的横向比较推荐一波卓同学的苹果 App Clip 技术详解一文。
![](https://i-blog.csdnimg.cn/blog_migrate/987f2eb2330ba1d235e66488c1bd1c99.png)
App Clips Experiences
App Clips Experiences 是 Session 着重介绍的内容之一,不妨这么理解「体验」:
用户通过 NFC 标签、二维码、Message 信息、Map 、Safari、Siri 建议等途径唤起 App Clips 应用程序,在未安装主应用的情况下,体验主应用中的某些服务和功能。App Clips 可视为为主应用程序导流的又一新途径,不仅仅是在使用过程中给出下载主应用的建议,一旦设备下载安装了主应用程序,之前 App Clips 得到的权限授权以及数据都将一并移入到新下载的主应用中。
那么如何来唯一标识你开发的 App Clip 提供的不同「体验」呢?答案是 URL 链接。开发者很容易联想到 iOS 9 引入的通用链接(Universal Links)功能,简单罗列对比下异同:
相同点:
两者都可以通过 URL 打开应用程序,代码层面对 URL 的解析、处理逻辑和流程相似;
不同点:
App Clip 体验 URL 必须在 App Store Connect 进行注册,更多详细配置点击 [Configure and Link Your App Clips]( "Configure and Link Your App Clips") 一文;通用链接则是需要先在开发者中心开启 Associated Domains 选项,接着在工程配置中 Associated Domains 一栏填入你想支持的域名,最后一步是将支持的 URL 以 JSON 格式写入到 apple-app-site-association 文件中,放到域名服务器下的 .well-known
子文件夹中,注意服务器必须支持 HTTPS。
了解了 URL 唯一标识 App Clip 提供的体验服务,有哪些途径用于唤醒 App Clip 呢?
NFC:「简单的说,就是一个标签,内部含电子元件,可以记录一些信息,通常1KB左右存储容量的就能满足常用需要。而当你用手机接近(开启NFC)标签时,NFC会自动扫描标签内所含有的信息,然后在手机上面显示标签信息或者执行标签所包含的命令。使用NFC可以提供一些便利,如到家时,你想连接家里的WiFI,然后登陆QQ等,你可以自己制作一个这样的标签,然后贴在家门口处,回家一打开门,用手机一扫描此标签,手机会自动执行上述命令。当然,你也可以发挥自己的想象力,如给手机上面安装远程开机功能,然后制作成这样的标签,回家一扫描,电脑自动打开,当然还有更多的创意,只要你能想得到」[1]
QR codes,熟知的二维码;
Maps
Siri 建议
Safari,同通用链接类似,需要修改 apple-app-site-association 配置文件,加入对 App Clip 的支持,这样当你打开某个注册过的链接时,顶部就会呈现用于唤起 App Clip 的横幅 UI
Message 应用中识别 App Clip 体验链接,然后以卡片形式呈现。
苹果将于年底推出 App Clip codes,结合了 NFC 的易用性,同时可视化图形展示,可以通过轻碰和扫描来快速识别。
![](https://i-blog.csdnimg.cn/blog_migrate/22e3afb61117ebf7c5427ea20919d430.png)
App Clips
App Clips 发布之时,想必部分 iOSer 当时第一反应就是“完了,又要适配新功能啦“。实际上为已有项目添加对 App Clip 的支持非常简单,就是新增一个 App Clip 应用程序的 target,请确保你已经安装了 Xcode12 beta 版本,那么在 New => Target
弹出的面板中会发现多了 App Clip 选项:
![](https://i-blog.csdnimg.cn/blog_migrate/d3fc735bc0e812be88a79e9b33d3ed01.png)
关于 App Clip,我们需要明确如下四点:
App Clip 是第一个独立的应用程序,包含了必要的功能代码和资源文件,专门负责处理「体验」服务请求;
App Clip 必须关联一个主应用程序,需要在 App Store Connect 中创建一个版本,和主应用一起提交审核,session 中提及 ”You cannot upload an App Clip or its app independently of one another“,这么看来两者都不能单独提审;
主应用程序和其关联的 App Clip 提交到 App Store 之后,两者就被分开存储,独立存在。假设用户通过上面某种途径打开「体验」链接,设备上没有安装主应用程序,那么系统就会下载 App Clip 到设备上,接着会被唤起调用;否则主应用程序总是第一选择,实际上用户一旦安装了主应用程序,系统就会移除掉 App Clip,因此请牢记一点:主应用程序也必须有处理 App Clip 体验链接的代码,否则一旦用户安装了主应用程序(此时 App Clip 会被系统移除),再识别体验链接时就会跳转至主应用程序进行处理,此时若未相关处理,就只是打开应用程序而已了;
App Clip 包大小限制在 10 MB,满足基本功能前提下包体积越小越好,某些资源文件的下载可适当延后,另外技术群里也提到 App Clip 是否可以作为一个壳,内容呈现是基于 WKWebview 展示,目前存疑;
关于 App Clip 的注意事项就是上述几点,那么我们如何基于主应用程序来开发和构建 App Clip 呢?通常市面上大部分应用程序都是基于 Tab Bar 控制器来划分功能模块,如下提供了四个功能模块:
![](https://i-blog.csdnimg.cn/blog_migrate/e49ebb92040e943e5bbdb8a2301d3254.png)
现在开始设计 App Clip 所需的功能,我们仅保留对外提供服务的代码和资源,去除类似用户信息面板等不必要的功能模块。
![](https://i-blog.csdnimg.cn/blog_migrate/eef399354d4991423f6ce18ddbd576f4.png)
显然 App Clip 旨在提供「体验」服务,我们希望唤起 App Clip 后,让用户快速到达目标功能,因此 Tab Bar 结构并不适用,调整如下:
![](https://i-blog.csdnimg.cn/blog_migrate/73b3c9fd3c0b3432fc1a74eeccb53420.png)
App Clips 负责处理体验链接,建议单次处理一个体验链接请求。
Xcode 上手
苹果官方提供了一个名为 Fruta 的 Demo ,用于演示如何在已有项目中新增对 App Clip 的支持,Demo 下载地址[2]。
上文说到已有项目添加对 App Clip 的支持非常简单,就是新增一个 App Clip 应用程序的 target。
![](https://i-blog.csdnimg.cn/blog_migrate/d3fc735bc0e812be88a79e9b33d3ed01.png)
选中 App Clip 新建一个 target,填写必要信息即可。
![](https://i-blog.csdnimg.cn/blog_migrate/738c2e2a97dfeeefa5110f0df6631a4e.png)
由于主应用程序(iOS or macOS)和 App Clip 某些资源文件是共用的,因此我们可以新建一个共享的 Asset Catalog ,此处注意勾选需要共享的几个 target。
![](https://i-blog.csdnimg.cn/blog_migrate/e68780a4b1942788a0bf486de9cc83ef.png)
接着将共用的资源文件都放入到新建的 Asset 文件夹下:
<<< 左右滑动见更多 >>>
接着我们要为 App Clip Target 按需「添加」主应用中已经实现的类、文件或是整个模块,对于文件来说,只需要在 Target Membership 中勾选上 Clip target 即可。
![](https://i-blog.csdnimg.cn/blog_migrate/adb483289e2ab99e282d1c383e97e736.png)
这里涉及到共用代码不同平台的区分处理问题,因此我们需要为 Clip target 的 Debug 和 Release 配置项中添加一个 APPCLIP 宏,如下所示:
![](https://i-blog.csdnimg.cn/blog_migrate/ec329f21b532ddfb3ed691ca59ce482d.png)
相应地,我们需要在代码中使用宏条件进行分支处理:
![](https://i-blog.csdnimg.cn/blog_migrate/e513148db95f2b744dc12e42357ce361.png)
此处可能有读者要问了,上文说到通用链接的处理和 App Clip 体验链接的处理两者类似,是如何解析链接处理请求呢?官方提供的 Demo 中 FrutaApp.swift
整个文件被 iOS 主应用程序和 App Clip 共享,免不了内部用 APPCLIP
宏来做分支处理,注意下面的 handleUserActivity
方法就是用来处理体验链接。
import SwiftUI
#if APPCLIP
import AppClip
import CoreLocation
#endif
@main
struct FrutaApp: App {
@StateObject private var model = FrutaModel()
#if !APPCLIP
@StateObject private var store = Store()
#endif
@SceneBuilder var body: some Scene {
WindowGroup {
#if APPCLIP
NavigationView {
SmoothieMenu()
}
.environmentObject(model)
.onContinueUserActivity(NSUserActivityTypeBrowsingWeb, perform: handleUserActivity)
#else
ContentView()
.environmentObject(model)
.environmentObject(store)
#endif
}
}
#if APPCLIP
func handleUserActivity(_ userActivity: NSUserActivity) {
guard let incomingURL = userActivity.webpageURL,
let components = NSURLComponents(url: incomingURL, resolvingAgainstBaseURL: true),
let queryItems = components.queryItems else {
return
}
if let smoothieID = queryItems.first(where: { $0.name == "smoothie" })?.value {
model.selectSmoothie(id: smoothieID)
}
guard let payload = userActivity.appClipActivationPayload,
let latitudeValue = queryItems.first(where: { $0.name == "latitude" })?.value,
let longitudeValue = queryItems.first(where: { $0.name == "longitude" })?.value,
let latitude = Double(latitudeValue), let longitude = Double(longitudeValue) else {
return
}
let region = CLCircularRegion(center: CLLocationCoordinate2D(latitude: latitude,
longitude: longitude), radius: 100, identifier: "smoothie_location")
payload.confirmAcquired(in: region) { inRegion, error in
if let error = error {
print(error.localizedDescription)
return
}
DispatchQueue.main.async {
model.applePayAllowed = inRegion
}
}
}
#endif
}
App Clips 技术概述总结
从下图来看,主应用程序和 App Clip 对比来看共性很多,因此对于 iOS 开发者来说,开发 App Clip 就是 A piece of cake ????。
![](https://i-blog.csdnimg.cn/blog_migrate/9b4144db43e45ad45f4de18171d2f893.png)
不过 App Clips 同主应用还是有稍许差别,主要有如下几点:
App Clip 应用的生命周期由系统接管,长时间未使用会被系统移除,包括存储的临时数据;
限制访问 Health、Fitness、通讯录、信息、照片、文件等个人数据;
当且仅当用户主动操作才能唤起 App Clip,或者是识别到 app clip URL 链接;
App Clip 中不支持通用链接(Universal links),document types 和 URL schemes;
App Clip 为了避免弹窗授权的糟糕体验,设计了免申请的通知、定位权限。当然还是有限制,免申请的通知只在 8 个小时内有效,地理位置只能获取一次;
此外还有一点需要强调,当用户感觉体验不错后,下载安装了你的主应用程序,此时系统会将之前下载安装的 App Clip 移除,然后将早前的授权权限和数据移入主应用程序中,最后删除 Group Container,之后再次打开体验链接的总是应用程序。
![](https://i-blog.csdnimg.cn/blog_migrate/3848e90c7d3c614df1c9272dc7532eec.png)
以上就是本session 的内容啦,最后非常感谢阅读此文,希望对您理解 App Clips 有些许帮助!
参考资料
[1]
NFC: https://hacpai.com/article/1365949196429
[2]Demo 下载地址: https://docs-assets.developer.apple.com/published/b7ada4cd51/FrutaBuildingAFeatureRichAppWithSwiftUI.zip
程序员专栏 扫码关注填加客服 长按识别下方二维码进群
近期精彩内容推荐:
在看点这里好文分享给更多人↓↓