App Clips 是今年 WWDC20 大会的亮点之一,罗列几个开发者感兴趣的问题:什么是 App Clips;实际应用场景中是如何交互的;构建 App Clips 的有哪些前置条件;如何在已有项目中添加对 App Clips 的支持等一系列问题。
在回答上述几个问题之前,我们先来明确三个概念:
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 呢?
苹果将于年底推出 App Clip codes,结合了 NFC 的易用性,同时可视化图形展示,可以通过轻碰和扫描来快速识别。
App Clips 发布之时,想必部分 iOSer 当时第一反应就是“完了,又要适配新功能啦“。实际上为已有项目添加对 App Clip 的支持非常简单,就是新增一个 App Clip 应用程序的 target,请确保你已经安装了 Xcode12 beta 版本,那么在 New => Target
弹出的面板中会发现多了 App Clip 选项:
关于 App Clip,我们需要明确如下四点:
关于 App Clip 的注意事项就是上述几点,那么我们如何基于主应用程序来开发和构建 App Clip 呢?通常市面上大部分应用程序都是基于 Tab Bar 控制器来划分功能模块,如下提供了四个功能模块:
现在开始设计 App Clip 所需的功能,我们仅保留对外提供服务的代码和资源,去除类似用户信息面板等不必要的功能模块。
显然 App Clip 旨在提供「体验」服务,我们希望唤起 App Clip 后,让用户快速到达目标功能,因此 Tab Bar 结构并不适用,调整如下:
App Clips 负责处理体验链接,建议单次处理一个体验链接请求。
苹果官方提供了一个名为 Fruta 的 Demo ,用于演示如何在已有项目中新增对 App Clip 的支持,Demo 下载地址[2]。
上文说到已有项目添加对 App Clip 的支持非常简单,就是新增一个 App Clip 应用程序的 target。
选中 App Clip 新建一个 target,填写必要信息即可。
由于主应用程序(iOS or macOS)和 App Clip 某些资源文件是共用的,因此我们可以新建一个共享的 Asset Catalog ,此处注意勾选需要共享的几个 target。
接着将共用的资源文件都放入到新建的 Asset 文件夹下:
接着我们要为 App Clip Target 按需「添加」主应用中已经实现的类、文件或是整个模块,对于文件来说,只需要在 Target Membership 中勾选上 Clip target 即可。
这里涉及到共用代码不同平台的区分处理问题,因此我们需要为 Clip target 的 Debug 和 Release 配置项中添加一个 APPCLIP 宏,如下所示:
相应地,我们需要在代码中使用宏条件进行分支处理:
此处可能有读者要问了,上文说到通用链接的处理和 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 Clip 对比来看共性很多,因此对于 iOS 开发者来说,开发 App Clip 就是 A piece of cake 。
不过 App Clips 同主应用还是有稍许差别,主要有如下几点:
此外还有一点需要强调,当用户感觉体验不错后,下载安装了你的主应用程序,此时系统会将之前下载安装的 App Clip 移除,然后将早前的授权权限和数据移入主应用程序中,最后删除 Group Container,之后再次打开体验链接的总是应用程序。
以上就是本session 的内容啦,最后非常感谢阅读此文,希望对您理解 App Clips 有些许帮助!
参考资料
[1]NFC: https://hacpai.com/article/1365949196429
[2]Demo 下载地址: https://docs-assets.developer.apple.com/published/b7ada4cd51/FrutaBuildingAFeatureRichAppWithSwiftUI.zip
Copyright© 2013-2020
All Rights Reserved 京ICP备2023019179号-8