Fatbobman 的 Swift 周报第153期:iPhone Duo 折叠屏带来的开发者机遇与挑战

iPhone Duo 的机遇与挑战

在线阅读→

尽管 iPhone Duo 设计的一些细节几个月前就已泄露,而苹果在折叠手机市场的进军较晚,但它能够紧密整合硬件和软件,以及由此带来的交互变化,仍然让不少消费者感到惊讶。

不同的研究机构对iPhone Duo的早期销量提供了不同时间框架的预测。Counterpoint预计到2026年底,出货量将达到多达600万台,而IDC估计该设备上市前12个月内可能达到约1000万台。

这仍然仅占iPhone总销量的一小部分。但与前两年半销量数十万台Vision Pro相比,这个市场已经足够大,支持许多专为该设备设计的应用。

过去几天,我已经在社交媒体上看到许多围绕Duo机型设计的创意创意。如果其中哪怕只有几款能成为突破性热门,也能进一步推动设备销量。

更重要的是,Duo的社交曝光度远高于Vision Pro。来自独特硬件和独特应用带来的“不同”感,正是许多早期采用者所追求的。

然而,对于大多数开发者来说,iPhone Duo独特形态带来的适应压力非常真实。Duo拥有内外屏幕,内屏可以完全开放、部分折叠或其他姿势使用。

它可以说是iPhone系列中设计上最具挑战性的设备之一。苹果确实提供了多个专门用于适应这些配置的API,标准容器已经能相当好地处理不同的姿势。

但这些主要解决了UI问题。真正的挑战是用户体验:API可以告诉你可用空间和铰链位置,但无法告诉用户在设备部分折叠时真正想看到什么。

每多一个姿势就意味着需要设计另一种体验。

对于使用第三方框架如Flutter和React Native的团队来说,问题更早就开始了:问题不仅在于你适应的能力,更在于你是否能访问必要的信息。

Flutter的MediaQuery.displayFeatures无论折叠角度如何,Duo都会返回一个空白列表,而React Native仍然没有官方API来访问折叠状态。

一旦硬件本身成为交互模型的一部分,跨平台框架就必须在隐藏平台差异和暴露平台能力之间做出更多权衡。而这些权衡对框架来说很难自行吸收——它总是落后于平台。

事实上,在Duo发布之前,重新评估跨平台方法的趋势已经开始,这主要是由于同时发生的多项变化。AI编码代理大幅降低了原生开发和维护的成本,而Liquid Glass和Duo等变革则持续提升充分利用平台特定能力的价值。

跨平台开发的成本效益平衡正被双向挤压。Notion正在将其苹果平台的UI从基于网页的技术栈迁移到SwiftUI。大约在同一时间,Shopify宣布将移动应用从React Native迁移回Swift和Kotlin。

其一个原因是编码模型的进步正在侵蚀跨平台开发的一个关键成本优势:避免“重复构建同一功能”。与此同时,Shopify也在交付其React Native开源库。

对于苹果生态系统来说,iPhone Duo 是一款带来真正变革的产品。过去几年,苹果平台的大多数变化都发生在软件和设计语言上。相比之下,Duo 改变了开发者必须设计的物理形态。

它对生态系统的重要性可能不在于多卖几百万设备,而在于当设备本身再次成为应用设计的一部分时会发生什么:“使用平台的原生框架”正开始从偏好问题转变为成本问题。

上一期通讯档案


📢赞助商:Fatbobman's Swift Weekly

向Swift和iOS开发者推广你的产品:

- 博客:50,000+ 月访客
- 通讯:5,000+ 订阅者,50开启率

非常适合开发者工具、课程和服务。

📢 查看赞助选项

喜欢这一期吗?请我喝杯咖啡☕️


近期建议

Swift调试信息中的模块跟踪

你有没有遇到断点,输入一个看似简单的LLDB表达式,然后等待......然后等待?为了评估计算出的属性或调用方法,LLDB有时需要启动Swift编译器,定位并加载相关模块。

过去,这种查找并不总能找到你代码构建的具体模块,甚至可能在过程中重新编译一些依赖关系,把简单的调试步骤变成长时间的停顿。

阿德里安·普兰特尔引入了模块跟踪机制,这一机制始于Swift 6.3,并在6.4中得到了进一步完善。新方法让调试信息能够准确告诉LLDB:“这些是构建实际使用的模块,它们所在的位置”,从而省去了不必要的搜索和重新编译。

Swift 6.4还改进了桥接头部处理,并显著简化了调试伪装——Darwin上的dSYM捆绑包不再包含二进制Swift模块,Windows和Linux上基于调试信息构建的二进制文件也受益。


Swift 6.4 中的新哈希可满足性

阿尔捷姆·米尔扎贝基安通过几种获得收益的标准库类型HashableSwift 6.4的符合性,基于两个提案:SE-0514(Dictionary.Keys,CollectionOfOne,EmptyCollection)和SE-0523(UnownedTaskExecutor).最接近日常发展的有Dictionary.Keys,现在可以直接进入一个Set或者作为词典键使用,无需先转换为其他类型。

语义上值得注意:两个Dictionary.Keys只要值包含相同的键,它们就是相等的——顺序既不参与比较也不进行哈希,这与词典自身身份的定义是一致的。

Dictionary.Values但未获得同样的处理,因为值可能被重复,正确表达需要多重集语义,而 Swift 目前不具备。这是一项小的补全工作,但使这些类型在泛型和集合语境中更自然地存在。


如何廉价列出大型模型:Vein vs SwiftData

两周前,在SwiftData:优化始于建模我讨论了“胖模型”给SwiftData列表带来的内存和性能压力,并试图通过拆分模型来减少单次查询实际加载的数据量。

米娅·科林我用那篇文章里的基准代码来演示静脉她正在构建的持久化框架,支持场级的懒散加载。

Vein 对模型的声明方式与 SwiftData 类似,默认表现也类似:属性被迅速加载,关系则是懒惰地加载。区别在于,标记属性时@LazyField推迟阅读,直到实际访问时才会阅读。

因此,在“仅标题列表”的情况下,Vein在没有任何模型分裂的情况下达到了不错的数字。


令人难过但真实:本地化的应用商店截图自我验证

自动化不断渗透到应用开发的每一个阶段。但实际上,难点往往不是实现自动化本身——而是让自动化证明它做了正确的事。

在自动生成本地化的App Store截图时,韦斯利·马特洛克发现他的西班牙语截图悄悄地回归英文,而整个测试运行仍显示成功。原因被证实为-testLanguage在Xcode 27下无法可靠地访问应用。

Wesley改用模拟器语言设置simctl并增加了一项检查,比较截图中的SHA-256,以发现两种语言或两个不同屏幕意外生成字节相同的图像的情况。

Wesley的截图哈希方法可以迁移到任何将请求交给黑盒子,然后按字面接受任何返回的流程。

过渡或内容转换

transition以及contentTransition它们名称极为相似,但解决了两个不同的问题:前者用于动画显示视图入或移除,后者则在视图保持原位且仅显示内容时生效。

斯图尔特·林奇通过条件视图、变化数字和SF符号并列比较,并涵盖常见的内容转换,如.numericText,.interpolate, 和.symbolEffect.

SwiftUI 支持自定义transition这些值很长时间了,但这种能力从未扩展到更高级的场景,比如表格或导航变更。这些情况类似于contentTransition,并且仍然主要依赖系统提供的少数过渡。希望SwiftUI最终能连接这些过渡概念的不同层次——或者至少给开发者更多空间进行定制。

用作用波抽象SwiftUI状态

当SwiftUI视图包含多个视图时@State我们通常将它们抽象为一个整体@Observable.但如果这种观点也依赖于环境价值或其他因素呢?Observable实例?

有没有办法组织来自不同来源的状态,同时减少视图与其中任何一个的耦合?迈克·阿普林提出以视野思考:他的图书馆ScopedState它在统一的访问边界后收集多个状态源,只向视图展示屏幕真正需要的状态和操作。

视图不需要知道数据来源,在预览和测试中更换数据源变得更加容易。值得注意的是:这并不是试图构建类似TCA或MVVM的另一种状态管理模型——而是在视图层组织和抽象现有状态源。


Fold之前:适应iPhone Duo

iPhone Duo的到来让消费者对这款全新的机型感到惊叹,开发者也面临一系列新的挑战:如何利用额外的显示空间,如何让现有布局在不同设备配置间适配,以及如何利用折叠和多重显示区域等新交互功能。

过去一周,多位作家分享了他们的看法。

上述许多功能仅存在于 Xcode 27.1 中,因此完整体验还需要一段时间。

工具

SwiftMusic:在Swift中宣告音乐

SwiftMusic,来自村本纪和是为苹果平台开发者设计的声明式音乐作曲框架。它借鉴了SwiftUI的设计理念,带来了熟悉的Swift结构——Music,Sound,body、结果构建器和修饰符——融入音乐创作中,使节奏、旋律、和声、音色、效果和混音关系都能通过作曲代码来描述。

例如,鼓、贝斯和合成器可以用Swift代码写成,音乐上如下:

struct Session: Music {
      var body: some Sound {
          Track(”Drums”) {
              Sample(”kick”)
                  .rhythm(”x ~ x ~”)
          }
​
          Track(”Bass”) {
              Synthesizer(.saw)
                  .notes(”C2 ~ Eb2 G2”)
                  .gain(0.3)
          }
      }
  }

SwiftMusic 本身不会发出声音:它只是将你的声明编译成节拍域事件和有序渲染计划。它既不打开音频设备,也不绘制编辑器——实际的音频渲染由主机负责。

作者提供音乐剧场,一个为macOS设计的实时编辑器,以填补这一角色。


Homebrew 7:告别英特尔的发布

Homebrew 7.0.0 已经发布。除了一系列功能和内部变更外,这次重大版本还带来了值得关注的兼容性变化。在苹果硅芯片 Mac 上,macOS 15–27 目前处于完全支持范围内,而 Intel Mac 则全部降至第三级——没有完整的 CI 支持,没有新瓶,计划在 2027 年 9 月或之后完全停止支持。

如果你仍在 Intel Mac 或较旧版本的 macOS 上运行 Homebrew,这款值得仔细看看。


感谢阅读Fatbobman的Swift Weekly!这篇帖子是公开的,欢迎大家分享。


iPhone Duo 带来的机遇与挑战

网页版

尽管 iPhone Duo 的设计资料早在几个月前就已部分泄漏,而且苹果也是折叠机领域的后来者,但凭借软硬件整合能力,它所带来的交互变化还是给不少消费者带来了惊喜。

不同机构对 iPhone Duo 的初期销量给出了不同口径的预测:Counterpoint 预计其 2026 年内出货量最高约 600 万部; IDC 则预计上市后前 12 个月可能达到约 1000 万部。 在整个 iPhone 销量中,这仍然只占一小部分,但与 Vision Pro 上市两年半的几十万销量相比,这个数量已经足以支撑不少特别针对这款设备开发的应用了。 这几天,我已经在社交媒体上看到不少针对 Duo 形态的创意,相信一旦其中出现几个爆款,也会进一步推动设备的销量。 尤其是,Duo 相比 Vision Pro 具备更强的社交展示性,特别的硬件、特别的应用所带来的“特别感”,正是不少首批购买这类产品的用户所追求的。

但对于更多开发者来说,iPhone Duo 独特形态带来的适配压力也是真实的。 Duo 包括内外两块屏幕,内屏又可以以展开、半折叠等多种姿态存在,可以说是 iPhone 家族中适配难度最高的产品之一。 苹果确实提供了不少有针对性的适配 API,标准容器在各种姿态下也已基本自适应,但这些只解决了 UI 层面的问题。 真正难的是 UX:API 能告诉你可用空间有多大、铰链落在哪里,却无法告诉你半折叠时用户究竟想看到什么。 多一种姿态,就多一次需要重新设计的体验。

对于使用 Flutter、React Native 等第三方框架的团队,问题还要更靠前一步:不是适配得好不好,而是拿不拿得到信息。 Flutter 的 MediaQuery.displayFeatures 在 Duo 上无论折叠到什么角度都返回空列表,React Native 则至今没有任何与折叠状态相关的官方 API。 当硬件本身开始成为交互的一部分,跨平台框架就必须在屏蔽平台差异与暴露平台能力之间做出更多取舍,而这种取舍很难由框架单方面消化——它总是滞后于平台。

事实上,在 Duo 发布之前,重新评估跨平台方案的趋势就已经出现,其背后是一组正在同时发生的变化:AI Coding Agent 大幅降低了原生开发与维护的成本,而 Liquid Glass、Duo 这样的变化又在不断抬高用足平台特性的价值。 跨平台方案的性价比,正被这两头同时挤压。 Notion 正在将基于 Web 技术栈的苹果端 UI 迁移到 SwiftUI; Shopify 则在几乎同一时间宣布将移动端从 React Native 迁回 Swift 与 Kotlin,理由之一是编码模型的进步正在削弱“避免把同一个功能造两遍”这一跨平台开发的重要成本优势。 与之一并发生的,是它对旗下 React Native 开源库的交接。

对于苹果生态来说,iPhone Duo 是一个真正带来变化的产品。 过去几年,苹果平台的变化更多发生在软件和设计语言上,而 Duo 则重新改变了开发者面对的硬件形态。 它的生态意义或许并不在于多卖出几百万台设备,而在于当设备本身再次成为应用设计的一部分时,“使用平台原生框架”这件事,正在从一道偏好题变成一道成本题。

前一期内容全部周报列表

如果您发现这份周报或我的博客对您有所帮助,可以考虑通过 请我喝杯咖啡 支持我的创作。

近期推荐

调试卡顿的根源:Swift 如何追踪 Module(Swift 调试信息中的模块追踪)

你是否碰到过这样的情况:一个看似简单的 LLDB 表达式,在断点调试时却迟迟没有反馈? 为了计算属性、调用方法等操作,LLDB 有时需要启动 Swift 编译器,并寻找和加载相关 Module。 过去这套过程并不总能准确找到构建时实际使用的 Module,甚至可能重新编译部分依赖,让一个原本简单的调试操作变成长时间等待。

阿德里安·普兰特尔 介绍了一个从 Swift 6.3 开始引入、并在 6.4 中进一步完善的机制:Module Tracking。 新的机制让调试信息能够更明确地告诉 LLDB:”构建时用的就是这些 Module,它们就在这里”,从而减少不必要的寻找和重新编译。 Swift 6.4 还会改善 bridging header 的处理,并让调试产物明显瘦身——Darwin 上的 dSYM 不再打包二进制 Swift Module,Windows 与 Linux 上带调试信息的二进制同样受益。


Swift 6.4 中补齐的几处 Hashable(Swift 6.4 中的新 Hashable 符合性)

阿尔捷姆·米尔扎贝基安 介绍了 Swift 6.4 中几个新增 Hashable 支持的标准库类型,它们分别来自 SE-0514(Dictionary.KeysCollectionOfOneEmptyCollection)和 SE-0523(UnownedTaskExecutor)。 其中最贴近日常开发的是 Dictionary.Keys:现在可以直接放入 Set 或作为 Dictionary 的 key,而不必先转换成其他类型。

值得留意它的语义:两个 Dictionary.Keys 只要包含相同的键就视为相等,顺序不参与比较与哈希——这与字典本身的身份认定是一致的。

Dictionary.Values 没有一并获得该能力,因为值可以重复,需要的是 Swift 目前没有的多重集合语义。 虽然只是一次小幅补全,但也让这些类型在泛型和集合场景中的使用更加自然。


Vein 中的惰值性能(如何廉价列出大型模型:Vein vs SwiftData)

两周前我在 SwiftData:优化从建模开始 中讨论了“胖模型”给 SwiftData 列表带来的内存和性能压力,并尝试通过模型拆分减少一次查询真正加载的数据。

米娅·科林 使用了我文章中的测试代码,展示了她开发的 静脉 持久化框架在字段级惰性加载下的表现。

Vein 采用了与 SwiftData Model 类似的声明方式,默认同样是属性预加载、关系惰性加载; 不同之处在于,给某个属性标注 @LazyField 后,它会推迟到真正被访问时才去读取。 因此在“只显示标题”的列表场景中,无需拆分模型也能取得不错的成绩。


让自动化自己证明它做对了(悲伤但真实:本地化应用商店截图自我验证)

自动化被越来越多地应用到 App 开发的各个环节,但有时实践中的难点并非如何实现自动化,而是如何让它自己证明“我做对了”。

韦斯利·马特洛克 在通过自动化方式为 App Store 生成多语言截图时,发现西班牙语截图悄悄回退成了英文,而整个测试流程依然通过。 检查后发现,原因来自 Xcode 27 下 -testLanguage 没有可靠地应用到 App。 Wesley 随后改用 simctl 设置 Simulator 的语言,并通过比较截图的 SHA-256,自动检查不同语言或不同页面是否意外生成了完全相同的图片。

Wesley 基于截图哈希的比较思路可以迁移到任何“把请求交给黑盒、再把产出照单全收”的流程里。

过渡或内容转换

transitioncontentTransition 名字很像,但解决的是两类不同的问题:前者用于 View 被插入或移出视图层级时的动画,后者则用于 View 仍然存在、只是显示内容发生变化的情况。

斯图尔特·林奇 通过条件视图、数字变化和 SF Symbols 等例子对两者进行了直观比较,并介绍了 .numericText.interpolate.symbolEffect 等常用的内容过渡效果。

在 SwiftUI 中,transition 很早就支持自定义,但这种能力始终没有延伸到 Sheet、导航切换等更高层级的场景。 这些场景与 contentTransition 类似,目前仍主要依赖系统提供的有限几种过渡方式。 期待未来 SwiftUI 能进一步打通这些不同层级的 transition 概念,或者至少为开发者提供更多自定义空间。

用 Scope 为视图状态划一条边界(Abstrating SwiftUI state with scopes)

在 SwiftUI 中,如果视图中有多个 @State,我们可能会将它们抽象到一个 @Observable 中。 但如果视图还依赖其他环境值或其他 Observable 实例呢? 是否有一种方式,可以将来自不同数据源的状态组织到一起,同时减少视图对具体数据源的耦合? ⁠迈克·阿普林 提出了 Scope 思路:通过他开发的 ⁠ScopedState 将多个状态源组织到统一的访问边界中,只向 View 暴露当前界面真正需要的状态和操作。 View 无需了解这些状态实际来自哪里,也更容易在 Preview 和测试中替换数据来源。 需要注意的是,这并不是试图创建另一套类似 TCA 或 MVVM 的状态管理模型,而是一种作用于 View 层级、对现有状态源进行组织和抽象的方式。


折叠之前:iPhone Duo 适配

iPhone Duo 的推出让消费者在惊叹设备形态变化的同时,也给开发者带来了新的挑战:如何利用更大的显示空间,如何让现有布局适应不同的设备形态,以及如何利用折叠、多显示区域等新的交互能力。 上周,不少内容创作者都从不同角度给出了自己的建议。

  • 李永俊 从现有 App 的实际适配出发,通过 NavigationSplitViewsidebarAdaptable、Device Hub 的 Resizing Mode 等方式,提前检查和改善 App 在 iPhone Duo 不同显示形态下的表现。
  • 萨加尔乌纳加尔 根据 ⁠Apple 最新的 HIG 和开发者资料,梳理了 iPhone Duo 在自适应布局、系统控件、折叠区域以及不同显示形态下的主要设计原则。
  • 乔丹·摩根 总结了开发者首先需要关注的布局、系统栏、safe area、相机以及多显示区域等变化。
  • 阿尔捷姆·米尔扎贝基安 介绍了新的 ArrangementView,开发者可以描述两组相关内容之间的关系,让 SwiftUI 根据可用空间和折叠区域自动选择合适的布局方式。
上面介绍的功能不少都只存在于 Xcode 27.1 中,想完整体验还需要一段时间。

工具

SwiftMusic:用 Swift 声明音乐

村本纪和 开发的 SwiftMusic 是一套面向 Apple 平台开发者的声明式音乐创作框架。 它借鉴了 SwiftUI 的设计理念,将 MusicSoundbody、Result Builder 和 Modifier 等熟悉的 Swift 表达方式带入音乐创作,让节奏、旋律、和声、音色、效果器和混音关系都可以通过组合代码来描述。

例如,鼓点、贝斯和合成器可以直接写成具有音乐含义的 Swift 代码:

struct Session: Music {
      var body: some Sound {
          Track(”Drums”) {
              Sample(”kick”)
                  .rhythm(”x ~ x ~”)
          }
​
          Track(”Bass”) {
              Synthesizer(.saw)
                  .notes(”C2 ~ Eb2 G2”)
                  .gain(0.3)
          }
      }
  }

SwiftMusic 本身并不发声:它只把声明编译成节拍域事件与渲染计划,既不打开音频设备,也不绘制编辑器,实际的音频渲染由宿主负责——作者另外提供了 macOS 端的实时编辑器 音乐剧场 来承担这部分工作。


Homebrew 7:一次告别 Intel 的大版本

Homebrew 7.0.0 正式发布,除了多项功能和内部实现调整,本次大版本也带来了值得关注的兼容性变化。 目前 Apple Silicon Mac 上的 macOS 15–27 属于完整支持范围,而 Intel Mac 已全部降为 Tier 3,不再获得完整 CI 支持和新的 bottles,并计划在 2027 年 9 月或之后彻底停止支持。 仍在 Intel Mac 或较旧 macOS 上使用 Homebrew 的开发者需要特别留意。

添加评论
点赞收藏
点踩分享查看原文
评论
?
参与讨论