简介: iOS 14 重磅推出了新性能 Widget,能够在主屏上展现一些要害信息,如日程、待办事项、设施电量等。Widget 的设计定位是什么?有哪些限度?如何进行 Widget 开发?本文基于一个小游戏——盒马小镇的 Widget 开发,分享在登录受权、数据更新、界面渲染以及审核上的实践经验。
Widget 简介
Widget 是 iOS 14 重磅推出的新性能,使得用户能够在主屏幕增加小组件,疾速浏览 app 提供的重要信息。它的设计与旧版本 macOS 的 Widget 一脉相承,甚至连增加的动画也是去掉了拟物化的水波纹成果。
设计定位
用户能够通过 Widget 对主屏幕进行个性化定制,然而 iOS 14 的 Widget 跟其余零碎上的小组件有很大的区别。在 Widget 的设计上苹果也放弃了一贯的克服,定位于轻量化、仅用作要害信息的展现。比方零碎自带 Widget 中的股票、天气、电量、静止信息,他们的独特特色是更新频率高、提供的信息重要,让用户不必关上 app 就能够浏览关怀的内容。苹果也不心愿开发者将 Widget 仅仅当作 app 的一个快捷入口,这样的需要更适宜用 contextual menu 来实现。
限度
苹果基于下面的设计定位,同时也为了节俭系统资源保障续航,对 Widget 的做了一些限度:
- 不反对动画,仅反对动态页面展现。
- 更新频率由零碎通过机器学习来动态分配。
- 不反对拖拽、滚动等简单的交互,不反对 Switch 等控件。
- 用户点击 Widget 肯定会跳转到 app。
Widget 开发实际
盒马小镇 Widget 设计
盒马小镇是盒马 app 内的一个小游戏,用户能够通过签到、购物、内容互动来获取盒花,用户最关怀的就是有没有盒花能够收取,或者是否有盒花能够帮摘等信息。这正好能够符合 Widget 的设计初衷,咱们冀望能借此晋升用户粘性。
要实现一个 Widget 首先要降级到 Xcode 12,而后在工程中减少一个新的 Widget Extension,Xcode 会主动生成须要的模板文件。
咱们当初要做的只有两件事:数据加载、渲染界面。
登录及受权
加载数据之前首先要解决的是受权问题。因为团体的登录 SDK 临时不反对 extension,加上全家桶依赖简单、体积很大,咱们不能应用 MTOP 来进行 API 调用,因而咱们设计了一套认证机制,主 app 在登录后调用 MTOP 接口获取 token,并且将 token 写入 app group 中,Widget 通过 HTTPS 接口加上 token 来拜访用户数据。
其中 token 通过 UserDefaults 来共享到 Widget,因为开发过程中不同的证书打包的 bundle id 不同,因而咱们将 group name 设置成 group.[bundle id] 的模式,保障能正确读取 token。
var token: String? {
let bundleIdentifier = Bundle.main.bundleIdentifier!
let defaults = UserDefaults(suiteName: "group." + bundleIdentifier)
return defaults?.object(forKey: "widget.town.homeinfo.token") as? String}
Token 的同步有几个机会:
- App 启动后,如果曾经登录并且 app group 中没有 token,则获取 token。
- 用户进入盒马小镇时获取 token。
- 用户登录时获取 token。
- 用户登出时删除 token。
数据更新
这里苹果借用了 timeline 的概念,timeline 外面蕴含了一个个 entry,entry 就是咱们在特定工夫须要展现的数据。零碎先回调咱们来获取 timeline 数据,再在特定工夫来回调咱们渲染界面。
咱们在返回 timeline 时能够设置更新策略:
- atEnd:在 timeline 中所有的 entry 都展现完之后更新。
- never:仅在主 app 触发更新。
- after:在特定工夫后触发更新。
当然这些都只是咱们倡议零碎的更新的机会,理论是否更新还是取决于零碎。对于能够预测信息的,比方天气预报,能够在 timeline 中增加多个 entry,并且抉择 atEnd 作为更新机会;对于不能够预测信息的,比方股市,能够抉择 after 策略,在肯定工夫后更新信息。
除此之外,咱们还能够在主 app 中调用 WidgetCenter 的办法来触发更新,经咱们测试应用这种办法每次调用都是可能真正触发数据更新的。更多信息能够参考官网文档 [1]。
在盒马小镇的场景下,咱们是无奈预知到未来的数据的,因而每次返回的 timeline 中只蕴含一个 entry,选取的策略是 after,每 20 分钟更新一次数据。
值得注意的是,在 getSnapshot 办法中也要返回实在的数据,这样用户在 Widget 的增加过程中看到的就是正确的界面,真正做到所见即所得。
在用户在主 app 中收取盒花之后,咱们还会被动触发一次数据的更新。
渲染界面
苹果为了推广 SwiftUI,规定 Widget 只能应用 SwiftUI 来编写界面。SwiftUI 与 Flutter 相似,是随 iOS 13 推出一套申明式的 UI 框架。SwiftUI 在这一年中有了很大的提高,补齐了很多能力上的有余。它使得跨平台(仅苹果生态)的界面开发变得非常简单。此外 SwiftUI 还反对 preview(相似 Flutter 的 hot reload),代码的改变能够近乎实时地反馈在 UI 上,极大进步了开发效率和体验。
如果有 Flutter 开发教训,只有相熟 VStack、HStack、ZStack 以及常见的几个 modifier 就能够轻松上手。
Bundle 拆分
与团体的大多数 app 一样,咱们的 app 工程也分为壳工程和业务 bundle,咱们不心愿每次批改 Widget 都去批改壳工程,于是要把 Widget 的业务逻辑拆分开来。通过尝试发现一个可行的方法:
- 新建一个 Swift framework bundle,将 Widget 实现放在外面,并且将外面的类设置成 public。
- 在壳工程中新建一个 Widget Extension,引入下面的 framework,在外面保留 @main 办法。
@main
struct HMTownWidget: Widget {
let kind: String = "HMTownWidget"
var body: some WidgetConfiguration {StaticConfiguration(kind: kind, provider: Provider()) { entry in
HMTownWidgetEntryView(entry: entry)
}
.configurationDisplayName("盒马小镇")
.description("疾速查看盒花动静,拜访盒马小镇收盒花")
.supportedFamilies([.systemSmall, .systemMedium])
}
}
这种办法能够缩小壳工程改变的危险,并且能够失常应用 SwiftUI 的 preview 和调试性能,毛病是 Widget 的配置信息仍在保留在壳工程中,临时没有更好的方法在保障调试性能的同时齐全拆散壳工程和业务代码。
Swift Bridge
后面提到在用户摘取盒花之后,咱们须要触发 Widget 的更新,这里须要调用 WidgetCenter 的 API,而这个 API 仅提供了 Swift 版本,而咱们的主 app 是纯 OC 写的,须要做一个 bridge。要实现 bridge 必须开启 Define Modules,而咱们的工程因为历史起因在开启后无奈编译通过,当初的解决办法是新建一个 Swift framework,外面调用 Widget Center API,再由业务去援用这个 bridge 的 framework。
埋点
Widget 的曝光事件咱们是无奈感知的,因为点击 Widget 会间接跳转到主 app,所以咱们在跳转到主 app 的 URL 上减少了埋点参数,主 app 解析 URL 中的参数调用 UT 来埋点。
审核
在咱们一开始打完包提交到 TF 审核时被拒了:
理由是咱们应用了有效的字体,这个字体是咱们在很早的版本就曾经依赖的 Flutter 三方库引入的,之前的审核也始终没问题,在去掉了依赖之后胜利过审,起因不明。Flutter 官网 issue[2] 提到只有在减少了 Widget 之后才会遇到这个问题。
Swift 的副作用
目前盒马 app 最低反对 iOS 9.0,而 iOS 直到 12.2 才将 Swift 根底库集成到零碎中,因而咱们须要在 Build Settings 外面将“ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES”设置为“YES”能力在 12.2 及以下的零碎上运行。
Swift 尽管在 5.0 版本实现了 ABI 稳固,然而在低版本操作系统中(iOS 12.2 以下)仍旧会有一些不够完满的中央。
在低于 iOS12.2 以下的操作系统会带来约有 3MB 的包大小问题,但侥幸的是苹果放开了蜂窝数据网络下 200M 的下载大小。
在低于 iOS12.2 以下的操作系统会有 100-200ms 不等的启动耗时减少,但在团队同学的致力下上线了二进制重排,启动性能大幅度回升。具体分析请查看:手淘架构组最新实际 | iOS 基于动态库插桩的⼆进制重排启动优化 [3]。
——《手淘 Swift 2019 大事记》
好在 iOS 的降级率比拟高,置信随着工夫的推移 Swift 的利用会越来越多。
最初
目前新版本曾经正式公布(4.54.1),欢送下载体验,App Clip 商品溯源也同步上线。当初的版本仍存在一些问题,比方用户如果曾经关上小镇页面,通过点击 Widget 关上 app 依然会再次关上小镇页面,须要从路由的层面优化;中尺寸的卡片绝对于小尺寸也并没有透出更多的信息,与苹果的设计初衷相违反。不过也算是对新技术的一次尝试,前面会继续优化,期待能产生业务上的一些增量。
相干链接
[1]https://developer.apple.com/documentation/widgetkit/keeping-a-widget-up-to-date
[2]https://github.com/flutter/flutter/issues/65991
[3]https://mp.weixin.qq.com/s/YDO0ALPQWujuLvuRWdX7dQ