简介
App Clip是苹果WWDC2020上推出的轻量版App,它是完整应用的一部分,在某个场景下为用户提供更快捷的体验。
App Clip有点类似于微信小程序,区别之处在于小程序是从0开始开发一个和完整App相同功能的应用,而App Clip是在App的基础上独立出一个或几个特色功能来供用户去使用的,之后由用户决定是否下载完整版的应用。
App Clip的触发方式
- App Clip码
- NFC标签
- 二维码
- 浏览器页面广告
- 信息中的链接
- 地图App里面的地点卡片
- 最近使用过的App Clip列表
App Clip的注意事项
- 主App只能有一个App Clip
- 主App必须支持App Clip的所有功能
- App Clip的size要控制在10MB之内,并且要尽可能的小
- 无法执行后台活动
- 无法使用NSURLSession进行后台网络请求
- 无法维护蓝牙连接
- 部分Frameworks受限,在编译时不会报错,但是运行时是不可用的
- Assets Library, Background Tasks, CallKit, CareKit, CloudKit, Contacts, Contacts UI, Core Motion, EventKit, EventKit UI, File Provider, File Provider UI, HealthKit, HomeKit, Media Player, Messages, Message UI, PhotoKit, ResearchKit, SensorKit, and Speech.
- 保护用户隐私方面
- SKAdNetwork不可用
- 不能请求追踪用户信息
- 不能随时获取用户位置信息
- 不能共享除主App之外其他App的数据
- 不能访问Apple Music 和 Media
- 不能访问日历,通讯录,文件,健康,消息,提醒和照片等应用程序的数据
- 不能访问运动和健康数据
- 登录
- 苹果建议最好避免创建账号,让用户能直接使用App Clip的核心功能,如果必须创建账号,建议使用Sign in with Apple来完成
- 支付
- 使用Apple Pay来完成
育学园在App Clip上的实践
育学园是一个母婴类的App,功能包括了浏览育儿知识,记录宝宝成长,健康指导,家长互动社区,甄选商城等一系列功能,我本人也有一个八个月的小宝宝,在育儿方面最常遇到的问题大概就是这个食物宝宝/宝妈到底能不能吃、什么时候能吃。前面也提到了App Clip其实是专注于完成主App上某一个特定功能的,而能不能吃功能非常符合App Clip的设计初衷,不用下载完整的App,也不需要用户登录,点击链接直达搜索界面,输入名字点击搜索,得到结果。因此决定先用能不能吃功能作为育学园App Clip的第一个功能入手开发。
创建App Clip
- 添加一个App Clip的Target
- 添加App Clip的Associated Domains Entitlement
这里和Universal Link时一样,applinks改成appclips即可
更新服务器apple-app-site-association文件 添加App Clip相关内容
{
"appclips": {
"apps": ["ABCED12345.com.example.MyApp.Clip"]
}
}
App Clip默认入口
- Smart App Banner
- 信息App中的文本链接
添加Smart App Banner的方式
<meta name="apple-itunes-app" content="app-id=myAppStoreID, app-clip-bundle-id=appClipBundleID, affiliate-data=myAffiliateData, app-argument=myAppArgument">
代码共享
实际上我们只需要把主工程的能不能吃相关代码共享给App Clip即可。
代码中用到的第三方库是使用pod管理的,修改podfile文件共享pod ```objectivec
There are no targets called “Shows” in any Xcode projects
abstract_target ‘Shows’ do pod ‘ShowsKit’ pod ‘Fabric’
Has its own copy of ShowsKit + ShowWebAuth
target ‘ShowsiOS’ do pod ‘ShowWebAuth’ end
Has its own copy of ShowsKit + ShowTVAuth
target ‘ShowsTV’ do pod ‘ShowTVAuth’ end end
- 共享pod后就是把相关业务代码通过Target Membership共享给App Clip,如果代码耦合比较严重,可以配合使用预编译宏对代码进行分隔,后续还是要对项目进行重构,模块化。
![macrosset.png](https://cdn.nlark.com/yuque/0/2021/png/423514/1609764780944-b2506749-1110-4df4-be95-a5ac29023fe5.png#align=left&display=inline&height=472&margin=%5Bobject%20Object%5D&name=macrosset.png&originHeight=472&originWidth=819&size=62545&status=done&style=none&width=819)
```objectivec
- (void)viewDidLoad {
[super viewDidLoad];
#ifdef CLIP
self.view.backgroundColor = [UIColor grayColor];
#else
self.view.backgroundColor = [UIColor whiteColor];
#endif
// Do any additional setup after loading the view.
}
资源共享
创建一个共享的Asset catalog来存放公共的图片资源
响应App Clip调用
- 添加相关代理拦截调起的URL
- 基于SwiftUI
- onContinueUserActivity(_:perform:)
- 基于UIKit,支持基于scene生命周期事件
- scene:continueUserActivity:
- 基于UIKit,响应基于app生命周期事件的
- application:continueUserActivity:restorationHandler:
- 基于SwiftUI
测试App Clip
Scheme中选中新建的target,把App Clip安装到手机上,如果编译报错看一下版本号和支持的设备是否和主工程一样
在创建完Target之后,Xcode默认会添加一个环境变量_XCAppClipURL,启用这个环境变量后,当运行App Clip时代理中就会拦截到这个测试的URL
添加本地入口
进入设置->开发者->APP CLIPS TESTING/Local Experience->Register Local Experience..
- URL PREFIX
准备拦截的URL,我们这里测试填写https://example.com
- BUNDLE ID
- App Clip Target的Bundle ID,默认是主App的Bundle ID加.Clip后缀
- Title,Subtitle,Photo
- App Clip Card展示的样式配置
- Action 可选值有Open,View,Play
- View
- 媒体或者提供信息,教育相关的内容
- Play
- 游戏类
- Open
- 除View,Play类型App之外的所有类型
- View
扫码测试
- 生成一个二维码,内容前缀为https://example.com
- 扫码调起App Clip Card,点击打开进入App Clip
推荐主App
苹果建议可以在App Clip完成任务后给用户推荐完整的App,下载完整应用后,App Clip就会被替代了。
// 使用SKOverlay实现
func displayOverlay() {
guard let scene = view.window?.windowScene else { return }
let config = SKOverlay.AppClipConfiguration(position: .bottom)
let overlay = SKOverlay(configuration: config)
overlay.present(in: scene)
}
查看App Clip大小
- Archive主工程后,打开Organizer窗口,选择archive,点击Distribute App
- 选择Ad-Hoc或者Development->选择App Clip->选择App Thinning,启用Bitcod
- 导出之后,查看文件夹中的size report文件
总结
以上是育学园App Clip从创建到测试进行的实践,开发阶段使用本地入口进行调试,发布测试阶段需要上传到TestFlight。技术本身并不复杂,需要写的代码也很少,主要工作在项目的解耦上,虽然项目中已经有部分进行了组件化,但是并不彻底,临时解决方案使用了预编译宏或条件编译分隔代码,减少了不必要的引入,后续还是需要继续对项目进行重构,模块化。