上个周末,我在北京参加了由苹果举办的 App 孵化器活动,本次的主题主要围绕 AI 时代的应用开发、运营和商业模式展开。与会者大多是 IT 公司的管理者和应用开发者,整个活动期间,大家对 AI 的热情都很高,也纷纷讨论各自对于 AI 的理解和应用技巧,我从中收获不少。
会议结束后,我和一位从事传统行业的朋友吃饭。他对 AI 基本一窍不通。作为一个新兴概念,他也想了解,但一直没有切实感受到 AI 究竟能给他的企业带来什么。就餐过程中,他接到一个电话,一个客户要求立刻调整方案。当时正值周末,即便马上把任务安排下去,也很难在短时间内完成。我在了解大致需求后,发现这恰好属于 AI 的舒适区,于是直接用手机让 AI 帮他处理。几分钟后,结果就出来了。
他不仅惊叹于 AI 完成任务的效率,也对结果的质量感到意外。而在他的公司里,恰好有一个由五六人组成的部门,大部分工作就是处理类似的任务。
于是,他马上开始向我询问更多细节,并计算如果引入 AI,可以缩减多少人力、降低多少成本。
这个转变之快让我有些诧异。我随即和他讨论了另一种可能:既然 AI 能以更低的成本完成过去的工作,那么是否也可以利用这些新增的能力去尝试以前因为成本过高而无法开展的业务?节流当然是 AI 最直观的价值,但开源同样是它可以施展的方向。
返程途中,我想的一直是那五六个人。他们此刻大概正安稳地过着周末,却并不知道自己的岗位刚刚在一顿饭的时间里被重新估价。而促成这次估价的,是我几分钟前的一次演示。
下一次遇到类似的场合,我大概还是会打开手机演示给对方看。但我想,我至少应该记得:屏幕这一侧的举手之劳,在屏幕的另一侧,或许是另一番境地。
近期推荐
Swift 嵌套闭包弱捕获警告 (Why Swift is introducing a warning for weak captures within nested closures)
弱捕获是 Swift 中重要的内存管理机制。一直以来,如果只在嵌套闭包的内层进行弱捕获,Swift 编译器可能会在父闭包中隐式插入强捕获。即便这种行为在某些场景下可能导致 retain cycle,编译器此前也不会给出提醒。从 Swift 6.4 开始,当编译器发现内层闭包的弱捕获与外层闭包的隐式强捕获存在差异时,将给出警告。
John Sundell 介绍了这一变化,并给出了解决方案:根据具体场景,在父闭包中显式声明引用方式;如果两层闭包都需要独立控制生命周期,则可以分别声明各自的捕获方式。这或许只是 Swift 编译器的一项小更新,但它能够帮助开发者更明确地表达嵌套闭包之间原本很容易被忽略的 ownership 关系。
提升 iOS 后台任务可靠性指南 (Running iOS Background Tasks Reliably)
BGTaskScheduler 从来都不是 iOS 上的 cron。后台任务真正何时执行,取决于系统调度、设备状态、能源预算以及用户使用 App 的习惯。Irving Popovetsky 通过实际设备上的 telemetry 长期观察后台任务的运行规律,分享了不少实践中踩过的坑:为了提高可靠性而频繁重新提交任务,可能反而把一个即将获得执行机会的任务不断向后推;force quit、长时间不打开 App、Low Power Mode、设备重启等用户行为和系统状态,都会影响后台任务获得执行机会;超过预定时间仍未执行,也并不意味着调度出现了异常。
对于后台任务,开发者努力的方向不应该是尝试精确安排执行时间,而是在系统愿意提供执行机会时,尽可能高效、正确地利用它。
TipKit 在真实项目中的落地实践 (The Memory Remains: TipKit in a Shipping App)
TipKit 学起来很容易,但想在实际项目中用好并不容易。真正的难点在于:什么时候不应该显示、如何避免干扰用户,以及怎样保证这套逻辑可测试、可维护。Wesley Matlock 在本文中分享了他在真实项目中的使用经验:@Parameter 更适合表示能够随时重新计算出的当前状态,而 Tips.Event 则适合记录无法从现有状态还原的历史行为;所有 tip 都应该避开仍可能出现 onboarding 或其他启动 UI 的阶段;Tips.configure 每个进程只能生效一次,因此初始化顺序、Debug 工具和测试环境都需要围绕这一限制设计。
TipKit 的精髓不在于外在的视觉效果,而在于内在的逻辑表达。它帮助开发者以声明的方式描述提示生成的规则,而提示的具体实现完全可以自定义。我们可以把 TipKit 想象成一个判断提示显示需求的规则引擎,至于如何可视化这些规则,则取决于开发者自己。更多关于这一视角的讨论,可以参阅 掌握 TipKit:进阶。
使用 Swift Charts 构建无障碍日历图表 (Building an accessible calendar chart with Swift Charts)
Matthaus Woolard 分享了如何用 Swift Charts 实现类似 GitHub Contributions 的日历图表。不过,文章更精彩的部分在于对 accessibility 的处理。由于不同周的相同 weekday 共享经过归一化的 x 值,VoiceOver 会将整列数据组合起来,而无法逐日浏览。作者通过 accessibilityChildren 为 plot area 另外构建了一套与视觉布局对应的 accessibility hierarchy,从而解决了这一问题。
由 Matthaus Woolard 和 Natalia Panferova 合著的 Swift Charts Beyond the Basics 一书已于上周正式发布。对于希望使用 Swift Charts 创建高级数据可视化的开发者来说,这是一本不可多得的进阶教程。
Mac 应用独立分发与 DMG 签名公证指南 (Mac App Direct Distribution, DMG Signing & Notarization Guide)
相比通过 Mac App Store 发布应用,直接分发 macOS App 需要开发者自己处理更多发布环节:Developer ID 签名、DMG 制作、notarization、staple 以及最终的 Gatekeeper 验证,任何一个步骤出现问题,都可能导致用户无法正常安装。Stewart Lynch 完整演示了一套从 Xcode Archive、制作 DMG,到最终签名、notarization 和验证的发布工作流。
在 SwiftUI 中优雅消费 SKIE Flow (Consuming SKIE Flows in SwiftUI)
在采用 Kotlin Multiplatform(KMP)共享业务逻辑、使用 SwiftUI 构建原生 UI 的项目中,如何让 Kotlin API 更自然地融入 Swift 生态一直是一个重要问题。SKIE(Swift Kotlin Interface Enhancer)正是为改善这一互操作体验而生,它可以将 Kotlin Flow 转换为 Swift AsyncSequence,让 Swift 端通过熟悉的 Swift Concurrency 方式消费数据。
Gustavo Fão Valvassori 介绍了 SKIE 针对 SwiftUI 提供的一组 API:除了直接在 .task 中通过 AsyncSequence 获取数据,还可以使用 .collect 将 Flow 的变化绑定到 SwiftUI state,或者通过 Observing wrapper 隐藏状态管理,让 Kotlin StateFlow 的最新值直接参与 View 的构建。
Sarunw 的 How to 系列回归
Sarunw 的博客一直是很多开发者获取 Apple 生态开发技巧的优秀资源。不过从 2023 年底开始,博客便停止了更新。上周,Sarunw 重新开始创作,并在一周内连续推出了数篇 How to 系列文章。很开心看到一位优秀内容创作者在沉寂近三年后重新回归。
工具
JindoKit:在 App 内预览灵动岛界面
WidgetKit 提供的 Live Activity 预览主要服务于 Xcode 中的开发与调试;对于允许用户选择主题、调整展示内容的应用,系统并未提供相应的 App 内预览方案。
由 Kyle Ye 开发的 JindoKit,将 WidgetKit 风格的 Dynamic Island API 带入普通 SwiftUI View,让 App 能够直接预览展开、紧凑和最小三种灵动岛形态。由于其 API 与 WidgetKit 的结构高度一致,开发者可以用相近的方式组织各个区域的内容,并在 App 配置界面与 WidgetKit Extension 之间复用相关视图和数据。更多信息请参阅作者的介绍文章:Preview Dynamic Island presentations in your app with JindoKit。
唯音输入法
由 Shiki Suen 开发的唯音输入法,是一款面向 macOS 的开源中文输入法。近期推出的「狂拼模式」,提供了类似中文之星智能狂拼、搜狗拼音、紫光拼音和升阳拼音(SunPinyin)的免输声调打字体验,部分词语还支持更激进的不完全拼写。
尽管支持类似狂拼模式的开源拼音输入法并不少见,但大多只支持汉语拼音。唯音借助自研的「齐铁恨(Tekkon)」注拼引擎,实现了对至少五种普通话拼音方案的狂拼打字支持。