盒马 iOS Live Activity &“灵动岛”配送场景实践
作者:陈振港、李凯
苹果在 WWDC22 中,提出了实时活动(Live Activity)的概念,以便于用户在锁屏查看一些应用实时活动的更新,这与盒马的配送场景十分贴合。因此,我们将 Live Activity & “灵动岛” 特性第一时间落地到盒马 30 分钟配送场景,并作为第一批适配实时活动的 iOS 应用,获得 App Store Today 推荐。
目前,盒马 App 适配“灵动岛”的最新 5.51.1 版本已正式上线,欢迎大家升级体验。在适配过程中,盒马 App 也在端侧踩过很多“坑”,特此汇总为实战经验分享给大家,欢迎交流。
快速落地时间线
6 月 16 日,根据 WWDC 相关内容提前调研;
8 月 16 日,开始基于官方文档和 beta 版做 demo;
9 月 8 日,登上 Apple 中国区官网;
9 月 15 日,iOS 16.1 beta 开放,开始正式的需求流程;
10 月 19 日,iOS 16.1 RC 上线,开始打包以及回归测验;
10 月 20 日,提交审核;
10 月 22 日,审核通过上架;
10 月 25 日,App Store Today 推荐。
客户端技术方案
Live Activity&灵动岛简介
在介绍盒马最终落地技术方案之前,先给大家简单介绍一下 Live Activity&灵动岛技术调研得出的一些关键信息及其比较适用的场景。
场景限制及建议
最多持续 8 小时,使用场景需要考虑,8 小时之后无法再刷新(目前实际还可以,但是以官方文档为准,自行限制),12 小时后强制消失(因此跨天场景不考虑)
创建时,需要 app 在前台主动创建,没启动应用的时候不能自己出现(与特定业务绑定,比如下单后显示)
卡片本身禁止定位以及网络请求,少量(4KB)数据可通过通知发送,或通过后台活动刷新数据
同场景多卡片由于样式趋同且折叠,不建议同时创建多卡片
灵动岛适配必要性
与锁屏 Live Activity 共享数据,在支持灵动岛的机型下,用户在非锁屏页面时,信息的更新会以灵动岛的形式展示更新
Live Activity 创建后,灵动岛就可以进行点击响应了,如果不适配的话,点击灵动岛会自动进入主程序,并且长按会变成一个没有任何信息的黑块
盒马 iPhone14 Pro 以及 Pro Max 用户占比逐渐升高
整体方案流程图
注意事项:
Live Activity 有一个独立的权限,并且是不需要请求同意的,默认开启
用户必须保持开始通知权限,才能通过通知刷新,后台活动不需要
必须在应用处于前台时启动,相当于要主动触发
数据更新时,系统自动适配动画,不需要自己额外添加动画(添加了也会被忽略)
不支持在其中显示 gif 图片和网络图片
可以设置响应页面,比如点击不同控件跳到不同页面,和 widget 类似
技术难点及策略
基于上述流程方案,我们在实际落地过程中也遇到一些问题,下面是核心问题和应对策略。
实时更新 - 通知刷新数据
更新方式有 2 种:
通过本地 update 方式在后台刷新数据,比如蓝牙、后台活动等。此方式要求主 App 进程必须存在,进程一旦杀掉就无法更新;
通过通知刷新的实时活动,需要通知为 Token-Based,并且对请求的参数做新的扩展。此方式比较灵活,无需主 App 进程存在,更新及时,因此我们采用此方式。
监听并上报 token
使用通知刷新的方式需要获取实时活动独立的 pushToken,对比在 Swift 中通过 Http 请求的方式上报,这里考虑用 block 的方式,将 Swift 中的 token 丢回给主工程,由主工程负责上传 token。
图片传入 - 非本地化图片
技术限制
因为 Live Activity 内部禁用网络图片,传统的服务端传图片 URL 的方式无法满足实际使用,但是希望传入订单图片来个性化地表达并且区分不同订单。
解决方案
iOS 16 beta 版时可以通过创建时将图片转为 Data 格式传入卡片,但是 iOS16.1 该方案仅限传入 4KB 左右的图片(API 限制),因此暂时不考虑非本地图片方案,采用内置图片方式实现。
机型适配 - 获取屏幕宽度问题
技术限制
iOS 16 beta 版时,尝试使用 UIScreen.main.bounds.width 获取屏幕宽度,该方法在模拟器上可以正确获取,但是在真机上,得到值为 0,导致无法进行适配。
解决方案
考虑在创建 Live Activity 时,直接从主工程将屏幕宽度作为起始属性传入其中,验证发现可行。
过渡动画 - 系统限制
Live Activity 限制了自定义动画,在数据刷新时,系统自行进行过渡,这也导致了过渡时会出现一些问题。
卡片大小变化过渡问题及解决方案
如果通过设置整个 View 的.background,在变更整体 Live Activity 大小的时候,一开始会先把之后的区域框定,但是超出的部分颜色与自定义的背景是不一致的。
改之前(渐变背景为.background 设置),有明显不同色的情况。
改之后(渐变背景为单独图层),变化时不再有颜色区别。
进度条长度变化过渡问题及解决方案
实际应用发现,通过 mask 实现的进度条,如果进度条变长,并没有过渡效果,而是直接跳到最终位置,因此考虑相反的进度条,就可以实现过渡效果。
埋点分析 - 点击跳转
由于默认情况点击是回主程序,而并不是固定页面,因此有必要自定义 widgetUrl(如用于回到订单页面),也可以通过 Link 实现分区域的跳转,Link 和 widgetUrl 共存时,点击 Link 区域会响应 Link,因此两者同时使用即可。
技术限制
无法在 widget 内部直接添加埋点,并且灵动岛收起时,仅支持添加同一个 widgetUrl,对于收起状态添加 Link 并没有响应。
解决方案
因为点击直接跳转到主 App,因此考虑将埋点参数加入 URL 参数即可,主 App 解析时埋点。但是无法记录包括用户查看、用户关闭(关闭卡片 继续发送推送也没有报错 因此无法判断)等行为的埋点。
对于灵动岛的区分,实际测试发现,在展开模式下,可以加入 Link 并且可以正常响应,这与官方文档中的描述一致。
版本适配 - 与小镇 widget 共存
盒马 App 本来上线有小镇 Widget,iOS 14 即可支持,而 Live Activity 需要 iOS 16.1,因此需要对两者做版本适配。
技术限制
Xcode 14beta 不支持在 Bundle 内部区分版本,只能重新创建一个扩展;
Xcode 14.1 支持在同一个 widget Extension 内部区分版本。但 beta 版发现使用了该方法,会导致两个 widget 都不出现,不使用则都可以正常使用,确认是当前版本 bug。
解决方案
后置系统版本判断,在 LiveActivityWidget 内部去限制版本,成功使二者同时展现。
触发场景 - 前置判断
权限限制
系统版本:
不支持 iOS 16.1 以下
没有权限:
通知权限
实时活动权限
前置权限限制判断,不满足以上条件时,不尝试创建 Live Activity。
时间限制
送达时间超过 8 小时的场景不创建,因为创建后 8 小时就无法再刷新
异常场景
已经存在了很多卡片,达到上限,创建时失败(返回创建结果,失败不进行后续推送)
用户关闭卡片,应用无法感知(继续推送,也不会再显示)
触发提醒 - 疲劳度控制
锁屏提醒
实时活动的刷新支持静默刷新和提醒刷新,这就需要考虑用户疲劳度,并且灵动岛的展开提醒会比普通的通知屏占比更大,更容易影响到用户。
带有 alert 字段的通知会触发锁屏的亮起以及声音,在主屏幕时会展示灵动岛,不带有 alert 字段的通知,会静默更新,锁屏时不会亮起,息屏显示时也不会有动画过渡,直接显示后续进度。
特殊情况
事件类型为 END 的通知,即使带了 alert,也不会触发提醒。如果需要的话,可以在此时发一条 update。
对于不支持灵动岛的机型,添加 alert 字段,会在主屏幕中触发锁屏 Live Activity 样式的横幅通知,目前该横屏会常驻显示,但屏幕有新的任何用户点击都会直接被收起。
扩展能力 - 字段解析
技术限制
为了保证通知能正常解析,通知 payload 字段必须保证包含客户端定义的字段,若新版本加了新字段,服务端下发了新字段,老版本不会受到影响也可以正常解析。
原因:原生的解析方式,将通知中的 JSON 解析为对象,但是对于字段缺少的 JSON,解析会失败,这时候无法成功刷新。
解决方案
考虑外部传入 JSONString,内部进行单独解析,注意不要解析为对象,不然还是没法处理字段缺少的情况,直接解析为字典,字典内部再对各个属性判断,并将空的属性置为空值,widget 内部再对应进行调整显示。
这样方式可以使各业务端只要传自己需要用到的字段即可,不必再传入空值。
后续技术规划
第一阶段我们在盒马即时配送场景进行初步探索,后续会持续关注用户使用反馈,不断优化迭代,把更好的购物体验带给大家。同时,持续关注苹果官方技术动态,尝试在更多与盒马紧密结合的业务场景落地,也欢迎热爱 iOS 新技术的同学与我们交流。
版权声明: 本文为 InfoQ 作者【阿里巴巴终端技术】的原创文章。
原文链接:【http://xie.infoq.cn/article/80d48c7af9a7d22cd74b46c3d】。文章转载请联系作者。
评论