目录
微注:本文首发于 vivo 快应用开发博客 - App Clips 与快应用。
App Clip
应用场景
官网在开发者文档中对 app clip 的叙述如下:它是一个轻量级版本的 app,当你在需要的时候和地方可以提供 app 的部分功能。比方说在 app store 上有一个 coffee-shop 的 app,这个 app 提供了“购买咖啡”,“保存喜欢的饮品”,“积分”,“特别优惠”等功能,而相应的 coffee-shop app clip 只提供“购买咖啡”的功能。
当用户走进咖啡厅,ios 根据 Siri 的地理位置建议或者 NFC 标签弹出 coffee-shop app clip 的使用建议(此时用户还没安装该 app),用户点击确认运行,系统会去下载 app clip 并立即运行,然后用户在上面完成“购买咖啡”的操作。
当然每次“invocation”触发的时候(这里的 invocation 包括:nfc,qrcode,safari app banner,短信的链接,地理位置建议,最近使用的 app clip),都会建议从 app store 中安装完整的 app。
引用官网的图片:
[图片上传失败...(image-58ee49-1597225746014)]
invocation(触发时机) -> app clip card(建议运行 app clip)-> app clip(运行)
此外,已知 invocation 有多种情况,相应的你可以每一个对应着一个 app clip card,但最终打开的都是唯一的 app clip。所以一个app只能对应一个app clip,但是一个app clip可以对应多个app clip card。
如果用户已经安装了 app,那么在 invocation 之后都是直接运行 app,而不会再显示 app clip 的建议了
swift 项目开发
作为一名业余的 swift 开发者,尝试了一下开发一个 app clip 的流程
文档中特别说明,如果是从头开始创建一个项目,那么要时刻记住自己将来要开发一个 app clip,所以你需要将能复用的部分尽量复用(好神奇哦,竟然能够复用)。
开始之前需要
- Xcode12 beta
- macOS Catalina 10.15.4 或者更新
- 如果想在手机上看效果的话还需要升级到 ios14,现在也是 beta 版本所以需要下载一个配置文件,这部分可以在网上搜教程,升级至 ios14
从 0 单排开始:创建一个项目 参考apple developer 教程
先创建一个single view app
,不过在 xcode12 中貌似没有这个选项了,只有一个app
的选项,创建之后文件结构如下:
<img src="https://zhanstatic.vivo.com.cn/wukong/img/f9e1d439-335c-4fa9-a0e6-ebc11198bfdd.jpg.webp" alt="drawing" style="width:100%;"/>
其中的
AppDelegate.swift
文件中看的几个方法从字面意义就知道他是做什么的,applicationWillResignActive
,applicationDidEnterBackground
,applicationWillEnterForeground
等等,看了就知道这里是 app 运行时的生命周期的几个 hook,有点小程序/快应用兼容版的app.js中的意思。ViewControler.swift
文件对应的某个页面的逻辑,有点小程序/快应用兼容版的page.js
的意思,打开文件看一下默认只有一个生命周期viewDidLoad
,是不是就是page.js中的onLoad
的意思,当然ViewControler.swift中还有更多的生命周期比方说viewDidAppear
和viewWillAppear
等等等等。。Main.storyboard
文件打开以后能看到一个手机屏幕,这个文件定义了你 app 的用户界面,在这个文件中会有多个“手机界面”,代表着你 app 会切换至多个界面,多个界面通过箭头连接,并且可以做一些navigation
过程中操作(此时我们只有一个界面,没有 transition)。还有一些常用的组件诸如 button、input、text、label 啥的直接可以拖拽到屏幕上,cmd + shift + l
就可以调出组件库,直接拖拽到界面上就可以了
<img src="https://zhanstatic.vivo.com.cn/wukong/img/941b7e7a-2a5c-45fc-bd56-50f6b74c2fd9.jpg.webp" alt="drawing" style="width:100%;"/>
此时可以注意到打开这个View Controler
的identity inspector
就是对应了 ViewControler 这个类,即你在ViewControler.swift
文件中定义的类,所以这个页面中的逻辑就是在ViewControler.swift
文件中。对应着一个可视化的小程序/快应用兼容版的 page.qxml/page.wxml 文件,感觉可拖拽比写 html 直观多了。
Assets.xcassets 文件对应了一些静态文件,比方说存放一些图片,可以新建 image set,每份图片上传三个分辨率1,2,*3,当运行在 iOS 上的时候系统会选出最合适的。(苹果的 UI 还是细致啊),还有专门的文档Human Interface GuideLines,这其中有专门介绍 app clip 的,包括 app clip card
LaunchScreen.storyboard 中的界面是当用户点击你 app 的 icon 之后首先展示的静态页面,在 app 启动之后才会进入到
Main.storyboard
中的界面info.plist 是一个 XML 文件包含一些 key、value 的数据对,一个 dictionary 用来存放数据的。
往 storyboard 上搞一些组件:
- 这一步简单,按照教程,我从组件库里拖了个
label
,一个text field
和一个button
到我的storyboard
上面
在逻辑层(ViewController.swift)中给组件们添加 Outlets
[图片上传失败...(image-74565-1597225746014)]
在 storyboard 中选中组件,按住control
进行拖拽到 swift 文件,然后会弹出一个框框,在其中输入名字,我这边跟着教程走,名字是 mealNameLabel,这时会生成如下的代码
@IBOutlet weak var mealNameLabel: UILabel!
document.getElementXXX (快应用中的SelectorQuery.select)有没有!相比与前端在 dom 中各种查找用选择器而言,这种方式感觉太强了。
绑定事件
[图片上传失败...(image-de3029-1597225746014)]
同添加 outlet 一样,control 拖拽 button 到ViewController.swift
文件中,同样弹出一个框框,在其中选择 Action,并填入 Action 的 name 就可以。
<img src="https://zhanstatic.vivo.com.cn/wukong/img/527fbf6f-5591-4835-ae2b-0552a1819a2c.jpg.webp" alt="controlDrag" style="width:100%;"/>
据说 xcode 的 control drag 会根据你拖拽至的代码 section 不同而判断你想要添加的是 outlet 还是 action,太强了。这一步就是addEventListener
有木有,此时我们在方法中添加一行代码:
@IBAction func setDefaultLabelText(_ sender: UIButton) {
// mealNameLabel是我之前label的outlet
mealNameLabel.text = "Default Text"
}
此时 run 你的项目,点击底下的 button 的时候,会将上面的"Meal Name"设置称为"Default Text", 相应的在快应用中的事件绑定,与 web 端和小程序的方法一致
给 text field 添加事件
这是有个新的概念叫做 delegate,官网的介绍:An object that acts on behalf of, or in coordination with, another object
,也就是说他是个代理。比方说我们的 text field,它有一个代理叫做“小秘”,当 text field 作为一个类似于 input 的组件, 用户会对他进行 input, focus, blur 等等,此时它会通知自己的小蜜,老子被 focus 了,然后小秘说知道了,然后执行一些 text field 的 hook(就是快应用中组件的事件绑定~)。
想要用“小秘”,要先让 ViewController 包含这个 delegate,简单,把 class 定义的那一行加上一个
UITextFieldDelegate
的 protocal,加完之后酱紫:
class ViewController: UIViewController, UITextFieldDelegate {
现在小秘有了,可以在里面让小秘干活了,添加下面这两个函数,字面意思就能看出来,方法textFieldDidEndEditing
是在完成 editing 的时候将 text field 的值赋给 label。
方法textFieldShouldReturn
是在用户敲 enter 的时候,触发textFieldDidEndEditing
方法。
func textFieldDidEndEditing(_ textField: UITextField) {
mealNameLabel.text = textField.text
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
// Hide the keyboard.
textField.resignFirstResponder()
return true
}
App Clip 开发
看到这里为啥连 app clip 的影子都没有提到过???别急,接着往下看,因为 app clip 没办法单独创建,它必须依靠一个现有项目,在这个项目下才能够新建一个 app clip。
创建一个 App Clip
同样在我们之前做的项目下,选择 File -> New -> Target, 然后在选择 App Clip
[图片上传失败...(image-c3fa1-1597225746014)]
我们只需要填 name 一项就可以了,剩下的都用系统默认生成的就可以了,完成之后我们定睛一看项目目录,居然和之前我们创建的新 app 一毛一样。
[图片上传失败...(image-191395-1597225746014)]
ps:项目中的 SceneDelegate.swift 本来在 FoodTracker 和 FoodTrackerClipsApp 中都应该有的,但是因为 FoodTracker 我用的是旧的 xcode 创建的,所以没有这个文件,但如果是新的 xcode 创建的话也应该有的。这个文件可以理解成是新的 ios 项目中将部分 AppDelegate 的功能移动到了 SceneDelegate 里面。
其次还多了个FoodTrackerClipApp.entitlements
的文件,这个是用来关联主程序需要配置的,默认的配置就可以了。
所以上面一大章讲的关于新建一个 app 的内容完全适用与开发 app clip,甚至于代码都可以来复用。
复用代码
这可能是开发 app clip 人员最喜欢的一点了,只要在原来的项目下创建好 app clip 项目,就可以在两个 target 之间公用代码,不需要再重新写一套,这也是为什么 apple 强调 app clip 一定要是原来 app 功能的子集,这里做了个实验,我们创建一个叫做 shareClass.swift 的文件,在里面随便写一个 Class 如下:
class Person {
var firstName: String?
var lastName: String?
let birthPlace: String
init() {
birthPlace = "France"
}
}
之后再在文件的 file inspector 中找到 target membership 中将我们的 app 和 app clip 全部勾选上
[图片上传失败...(image-71d712-1597225746014)]
这将意味着在 app clip 和 app 中都会加入这个文件进行编译(即使你复用了代码,最终这两个独立的文项目其实也是分别各自编译的,所以注意一下项目的大小不能超过 10mb),之后我们在 app 和 app clip 中的 ViewController 中分别创建 person 的实例,并打印其中的一个属性
let person = Person()
let birth = person.birthPlace
print(birth)
在 output 面板发现都会打印France
出来。
除此之外官方文档还提供了一个方式控制两个项目之间的代码共享,就是利用Active Compilation Conditions
,其实就是我们前端经常在用打包工具时设置一个 node 的 env 环境变量,然后在代码中加入判断条件,在项目设置的 build setting 中查找 custom 就能找到相关配置
[图片上传失败...(image-c7c441-1597225746014)]
#if !APPCLIP
// Code you don't want to use in your app clip.
#else
// Code your app clip may access.
#endif
提交 app 和 app clip
这个过程暂时还没办法体验,但是根据官网说的在提交的时候会配置你的 app clip card(card 和 app clip 是两个东西,app clip card 是半屏显示的,在 card 显示的过程中已经开始下载 app clip 了,用户点击完 open 之后打开的才是 app clip),其中有三个选项分别对应 card 的头部图片,一个标题,还有一个是那个按钮里面的动词,可以是open
,play
,或者View
之类的
[图片上传失败...(image-91cb5-1597225746014)]
另外在 app clip 首次启动的时候,在最上面也会显示一个 banner,提示用户去下载完整 app,另外在 clip app 开发过程中,我们也可以自己决定在某个时间点提示用户去下载完整 app。所以综上所述,有三个时间点都是希望提醒用户下载完整 app 的:1.app clip card 弹出的时候、2.首次运行 app clip 的时候、3.还有在用户使用的过程中。但是对于第三点 apple 说希望开发者注重一下用户的体验,不要在一些关键操作的过程中或者是频率太高的提醒用户下载完整 app。
快应用
基于手机硬件平台,由国内各大主流手机厂商组成的快应用联盟联合制定,与手机系统深度结合,手机厂商提供流量支持,直达用户,并且使用原生渲染,前端技术开发栈,还对个人开发者开放。
快应用与快应用兼容版
快应用在今年初推出了兼容版即小程序转换工具,面向对象是有小程序开发经验的开发者,能够无缝接入快应用生态,利用现有的项目从小程序直接接入快应用生态,当然也可以利用小程序语法从零开始编写新的项目。
快应用卡片卡开发
详情可以查看快应用卡片开发文档,与 app clip 不同的是需要单独创建项目,但是快应用 ide可以非常快速的创建新快应用或者卡片项目并打包上传
快应用项目开发
详情可以查看快应用开发文档
快应用和 App clip 的对比
入口场景
- app clip:NFC/扫码,在地图 app 的位置卡片,Message App 中(短信分享),Siri 根据地理位置的建议,网站中的 smart app banner:
[图片上传失败...(image-2b0c99-1597225746014)] - 快应用/兼容版:扫码,URL,负一屏,全局搜索,语音助手,应用商店,浏览器,系统桌面, 安全中心,垃圾清理,信息助手, 广告,push,短信,详情可以看文档
app clip 无法留存桌面,但是在 ios14 中会在 app library 中留下最近使用的 app clip,快应用可以在桌面留存,并且提供主动加桌接口
技术栈
- app clip: Object-C / swift
- 快应用/兼容版:前端技术栈
跳转能力
- app clip:只支持跳转至该 app clip 绑定的完整 app(没安装的话需要先安装,安装过后将不会再打开 app clip)
- 快应用/兼容版:支持从快应用跳转至任意原生 app,快应用跳转至快应用
渲染类型
- app clip: apple 原生
- 快应用:Android 原生(虽然技术栈是前端,但最终渲染是原生渲染)
- 快应用兼容版:Hybrid(Android 原生和 Web 结合)
程序大小
- app clip:10M 以内,注意在开发的时候原生 app 和 app clip 共享的代码并不会减少体积,最终打包是会分开打包的。
- 快应用/快应用兼容版:整个快应用的所有分包大小不超过 4M,单个分包/基础包大小不能超过 1M
卡片能力
- app clip 在特定场景下会主动弹出卡片,且卡片定制能力只包含:title,按钮中的动词,头部图片。在完整 app 被安装之后,再次被触发时会主动打开完整 app
- 快应用卡片可长期存在负一平中,并且可定制各种样式及功能,为单独项目并需要进行提交审核。
相对而言快应用中卡片对于快应用的关系和 app clip 对于 apple app 的关系类似。前者为后者功能的子集
系统能力
app clip:由于 apple 一再强调将完整的功能留给你的 app,所以在 app clip 中许多功能受限,比如说
AppTrackingTransparency
,其中的运营商和姓名等信息返回的都是空字符串。同时 app clip 没有后台运行能力,比方说在后台启动蓝牙,和访问网络,还有连续性的请求地理位置。app clip 也没办法获得手机的诸如“健康”,“媒体/图片/照片/音乐”,“联系人”,“短信”等各种信息。它还没办法和其他的 app 传递数据,除了和它绑定的完整 app。快应用/兼容版:大部分系统能力完全支持,因为快应用内核是与手机系统绑定,但是需要用户的同意
支付能力
- app clip: 只支持 apple pay
- 快应用:微信/支付宝/手机平台方
- 快应用兼容版:微信支付
账户登录能力
- app clip: sign in with apple
- 快应用:微信账户/微博账户/qq 账户
- 快应用兼容版:目前支持手机厂商账号登录
分享功能
- app clip: 苹果的 Message app 中进行分享
- 快应用:微信分享/微博分享/qq 分享
后台推送
app clip 提供后台推送,但是限时最多 8 个小时,即用户从每次关闭 app clip 之后的 8 个小时之内,作为开发者可以在 8 个小时之内推送,如果设置了推送的话在启动 app clip card 的时候会有一个小提示告诉用户该 app clip 会进行推送,当然用户可以选择拒绝
<img src="https://zhanstatic.vivo.com.cn/wukong/img/63760793-3614-4707-9c11-14f02b9c8a37.jpg.webp" alt="push" style="width:100%;"/>
如果超过 8 个小时之后的推送则需要另外申请,此时的申请提示和 app 一样,不会再 app clip card 上显示,而是在系统中显示
[图片上传失败...(image-225f74-1597225746014)]快应用也提供后台推送,当用户同意推送时即可实现推送,并且没有时间长短限制。
总结
读了文档+体验下来,感觉苹果的 app clip 注重的是在必要的时候省去安装完整 app 的等待时间,但最终的目的是将用户导流至完整 app。有一点像在外面报的兴趣班上课前的试听课,你先来体验体验,试听课肯定老师没办法拿出全部本事,但是如果你满意了试听课,那么交钱买下完整课程。
而微信小程序和快应用的目的是尽可能替代原生 app,更像是希望能够用网课代替线下的课程。
微信小程序和快应用的不同点在于微信是希望能够将用户保存在自己的生态圈:超级 app 微信中,而快应用则是利用其与系统的深度结合,在必要的时候迅速提供用户所需的功能。