The Opportunities and Challenges of iPhone Duo
Although some details of the iPhone Duo’s design had already leaked months ago, and Apple is a latecomer to the foldable phone market, its ability to tightly integrate hardware and software—and the interaction changes that integration enables—still managed to surprise quite a few consumers.
Different research firms have offered forecasts for the iPhone Duo’s early sales using different time frames. Counterpoint expects shipments to reach as many as 6 million units by the end of 2026, while IDC estimates that the device could reach around 10 million units in its first 12 months on the market. That would still represent only a small fraction of total iPhone sales. But compared with the hundreds of thousands of Vision Pro units sold over its first two and a half years, it is already a large enough market to support quite a few apps designed specifically for this device. Over the past few days, I’ve already seen plenty of creative ideas on social media built around the Duo’s form factor. If even a few of them turn into breakout hits, they could further drive device sales. More importantly, the Duo has far greater social visibility than Vision Pro. The sense of being “different” that comes from distinctive hardware paired with distinctive apps is exactly what many early adopters of products like this are looking for.
For most developers, however, the adaptation pressure created by the iPhone Duo’s unique form factor is very real. The Duo has both outer and inner displays, while the inner display can be used fully open, partially folded, and in other poses. It is arguably one of the most challenging devices in the iPhone family to design for. Apple does provide a number of APIs specifically for adapting to these configurations, and standard containers already handle different poses reasonably well. But those things mainly solve the UI problem. The real challenge is UX: an API can tell you how much space is available and where the hinge is, but it cannot tell you what users actually want to see when the device is partially folded. Every additional pose means another experience that needs to be designed.
For teams using third-party frameworks such as Flutter and React Native, the problem starts one step earlier: it is not just about how well you can adapt, but whether you can access the necessary information in the first place. Flutter’s MediaQuery.displayFeatures returns an empty list on the Duo regardless of the fold angle, while React Native still provides no official API for accessing fold state. Once the hardware itself becomes part of the interaction model, cross-platform frameworks have to make more tradeoffs between hiding platform differences and exposing platform capabilities. And those tradeoffs are difficult for a framework to absorb on its own—it will always lag behind the platform.
In fact, the trend toward reevaluating cross-platform approaches had already begun before the Duo was announced, driven by several changes happening at the same time. AI coding agents have substantially reduced the cost of native development and maintenance, while changes such as Liquid Glass and Duo continue to raise the value of taking full advantage of platform-specific capabilities. The cost-benefit equation for cross-platform development is being squeezed from both directions. Notion is migrating its Apple-platform UI from a web-based technology stack to SwiftUI. Around the same time, Shopify announced that it was moving its mobile apps from React Native back to Swift and Kotlin. One reason it cited was that advances in coding models are eroding one of the key cost advantages of cross-platform development: avoiding having to “build the same feature twice.” Alongside that transition, Shopify is also handing off its React Native open-source libraries.
For the Apple ecosystem, iPhone Duo is a product that brings genuine change. Over the past few years, most changes to Apple’s platforms have taken place in software and design language. Duo, by contrast, changes the physical form factor developers have to design for. Its significance to the ecosystem may not lie in selling a few million more devices, but in what happens when the device itself once again becomes part of application design: “using the platform’s native frameworks” is starting to shift from a matter of preference to a matter of cost.
Previous Issue|Newsletter Archive
📢 Sponsor Fatbobman’s Swift Weekly
Promote your product to Swift & iOS developers across:
- Blog: 50,000+ monthly visitors
- Newsletter: 5,000+ subscribers, 50% open rate
Perfect for developer tools, courses, and services.
Enjoyed this issue? Buy me a coffee ☕️
Recent Recommendations
Module Tracking in Swift Debug Info
Have you ever hit a breakpoint, typed a seemingly trivial LLDB expression, and then waited... and waited? To evaluate a computed property or call a method, LLDB sometimes has to spin up the Swift compiler and locate and load the relevant modules. In the past, that lookup couldn’t always find the exact module your code was built from, and it might even recompile some dependencies along the way, turning a simple debugging step into a long pause.
Adrian Prantl introduces Module Tracking, a mechanism that began in Swift 6.3 and is further refined in 6.4. The new approach lets debug info tell LLDB precisely: “these are the modules the build actually used, and here is where they live,” cutting out unnecessary searching and recompilation. Swift 6.4 also improves bridging header handling and noticeably slims down debug artifacts — dSYM bundles on Darwin no longer carry binary Swift modules, and binaries built with debug info on Windows and Linux benefit as well.
New Hashable conformances in Swift 6.4
Artem Mirzabekian walks through several standard library types that gain Hashable conformance in Swift 6.4, coming from two proposals: SE-0514 (Dictionary.Keys, CollectionOfOne, EmptyCollection) and SE-0523 (UnownedTaskExecutor). The one closest to everyday development is Dictionary.Keys, which can now go straight into a Set or serve as a dictionary key without first being converted to another type.
The semantics are worth noting: two Dictionary.Keys values are equal as long as they contain the same keys — order participates in neither comparison nor hashing, which is consistent with how a dictionary’s own identity is defined. Dictionary.Values did not receive the same treatment, because values can repeat, and expressing that correctly would require multiset semantics that Swift doesn’t currently have. A small piece of completion work, but one that makes these types sit more naturally in generic and collection contexts.
How to list big models cheaply: Vein vs SwiftData
Two weeks ago, in SwiftData: Optimization Starts with Modeling, I discussed the memory and performance pressure that “fat models” put on SwiftData lists, and tried to reduce how much data a single query actually loads by splitting the model. Mia Köring took the benchmark code from that article and used it to show how Vein, the persistence framework she is building, performs with field-level lazy loading.
Vein declares models much the way SwiftData does, and by default behaves similarly too: properties are eagerly loaded, relationships lazily. The difference is that marking a property with @LazyField defers reading it until it is actually accessed. As a result, in a “title-only list” scenario, Vein reaches good numbers without any model splitting at all.
Sad But True: Localized App Store Screenshots That Verify Themselves
Automation keeps working its way into every stage of app development. But in practice, the hard part often isn’t implementing the automation — it’s getting it to prove that it did the right thing.
While generating localized App Store screenshots automatically, Wesley Matlock discovered that his Spanish screenshots had quietly fallen back to English, while the entire test run still reported success. The cause turned out to be -testLanguage not reliably reaching the app under Xcode 27. Wesley switched to setting the simulator’s language through simctl, and added a check that compares the SHA-256 of the captured screenshots to catch cases where two languages, or two different screens, unexpectedly produce byte-identical images.
Wesley’s screenshot-hashing approach transfers to any pipeline where you hand a request to a black box and then accept whatever comes back at face value.
Transition or ContentTransition
transition and contentTransition have remarkably similar names, but they solve two different problems: the former animates a view being inserted into or removed from the view hierarchy, the latter applies when the view stays put and only the content it displays changes. Stewart Lynch compares them side by side using conditional views, changing numbers, and SF Symbols, and covers common content transitions such as .numericText, .interpolate, and .symbolEffect.
SwiftUI has supported custom
transitionvalues for a long time, but that capability has never extended to higher-level scenarios like sheets or navigation changes. Those situations resemblecontentTransition, and still rely mostly on the handful of transitions the system provides. It would be good to see SwiftUI eventually connect these different levels of the transition concept — or at least give developers more room to customize them.
Abstracting SwiftUI state with scopes
When a SwiftUI view holds several @State properties, we often abstract them into a single @Observable. But what if the view also depends on environment values or other Observable instances? Is there a way to organize state coming from different sources while reducing the view’s coupling to any particular one of them? Mike Apurin proposes thinking in scopes: his library ScopedState gathers multiple state sources behind a unified access boundary, exposing to a view only the state and actions that screen genuinely needs. The view doesn’t need to know where any of it comes from, and swapping the data source in previews and tests becomes much easier. Worth noting: this isn’t an attempt to build yet another state management model along the lines of TCA or MVVM — it’s a way of organizing and abstracting existing state sources at the view layer.
Before the Fold: Adapting for iPhone Duo
The arrival of iPhone Duo has consumers marveling at the new form factor, and developers facing a new set of challenges: how to make use of the extra display space, how to make existing layouts adapt across device configurations, and how to take advantage of new interaction capabilities like the fold and multiple display regions. Over the past week, a number of writers offered their perspectives.
Apple published six Tech Talks on announcement day: Design for iPhone Duo, Prepare your app, Raise the bar, Strike a pose with adaptive layouts, Leverage multiple displays and scenes, and Build a great camera experience, along with a new Designing for iPhone Duo page in the HIG.
Florian Schweizer makes the case for not fighting the framework: don’t start by asking whether the device is an iPhone Duo, ask how much space this view has been given. He has also distilled these principles, along with Duo’s new APIs and Apple’s developer material, into a SwiftUI iPhone Duo Skill for coding agents working on existing apps.
Lee young-jun approaches it from hands-on adaptation work, using
NavigationSplitView,sidebarAdaptable, and Device Hub’s Resizing Mode to inspect and improve how an app behaves across iPhone Duo’s different display configurations ahead of time.Sagar Unagar draws on Apple’s latest HIG and developer material to summarize the main design principles for iPhone Duo: adaptive layout, system controls, the fold region, and the various display configurations.
Jordan Morgan rounds up the changes developers should look at first — layout, system bars, safe areas, camera, and multiple display regions.
Artem Mirzabekian introduces the new
ArrangementView, which lets you describe the relationship between two related pieces of content and leaves it to SwiftUI to pick a suitable layout based on available space and the fold region.
Many of the features covered above exist only in Xcode 27.1, so experiencing them in full will take a little while yet.
Tools
SwiftMusic: Declaring Music in Swift
SwiftMusic, from Norikazu Muramoto, is a declarative music composition framework for Apple platform developers. It borrows SwiftUI’s design sensibility, bringing familiar Swift constructs — Music, Sound, body, result builders, and modifiers — into music creation, so that rhythm, melody, harmony, timbre, effects, and mixing relationships can all be described by composing code.
Drums, bass, and synths, for instance, can be written as Swift code that reads musically:
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 makes no sound on its own: it only compiles your declarations into beat-domain events and an ordered render plan. It neither opens an audio device nor draws an editor — actual audio rendering is the host’s responsibility. The author provides MusicPlaygournd, a live editor for macOS, to fill that role.
Homebrew 7: The Release That Says Goodbye to Intel
Homebrew 7.0.0 is out. Alongside a range of features and internal changes, this major release brings compatibility changes worth paying attention to. On Apple Silicon Macs, macOS 15–27 currently falls within the fully supported range, while Intel Macs have all been moved down to Tier 3 — no full CI support, no new bottles, and a plan to end support entirely in or after September 2027. If you are still running Homebrew on an Intel Mac or an older version of macOS, this one deserves a closer look.
Thanks for reading Fatbobman’s Swift Weekly! This post is public so feel free to share it.
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 则重新改变了开发者面对的硬件形态。它的生态意义或许并不在于多卖出几百万台设备,而在于当设备本身再次成为应用设计的一部分时,“使用平台原生框架”这件事,正在从一道偏好题变成一道成本题。
如果您发现这份周报或我的博客对您有所帮助,可以考虑通过 Buy Me a Coffee 支持我的创作。
近期推荐
调试卡顿的根源:Swift 如何追踪 Module (Module Tracking in Swift Debug Info)
你是否碰到过这样的情况:一个看似简单的 LLDB 表达式,在断点调试时却迟迟没有反馈?为了计算属性、调用方法等操作,LLDB 有时需要启动 Swift 编译器,并寻找和加载相关 Module。过去这套过程并不总能准确找到构建时实际使用的 Module,甚至可能重新编译部分依赖,让一个原本简单的调试操作变成长时间等待。
Adrian Prantl 介绍了一个从 Swift 6.3 开始引入、并在 6.4 中进一步完善的机制:Module Tracking。新的机制让调试信息能够更明确地告诉 LLDB:”构建时用的就是这些 Module,它们就在这里”,从而减少不必要的寻找和重新编译。Swift 6.4 还会改善 bridging header 的处理,并让调试产物明显瘦身——Darwin 上的 dSYM 不再打包二进制 Swift Module,Windows 与 Linux 上带调试信息的二进制同样受益。
Swift 6.4 中补齐的几处 Hashable (New Hashable conformances in Swift 6.4)
Artem Mirzabekian 介绍了 Swift 6.4 中几个新增 Hashable 支持的标准库类型,它们分别来自 SE-0514(Dictionary.Keys、CollectionOfOne、EmptyCollection)和 SE-0523(UnownedTaskExecutor)。其中最贴近日常开发的是 Dictionary.Keys:现在可以直接放入 Set 或作为 Dictionary 的 key,而不必先转换成其他类型。
值得留意它的语义:两个 Dictionary.Keys 只要包含相同的键就视为相等,顺序不参与比较与哈希——这与字典本身的身份认定是一致的。Dictionary.Values 没有一并获得该能力,因为值可以重复,需要的是 Swift 目前没有的多重集合语义。虽然只是一次小幅补全,但也让这些类型在泛型和集合场景中的使用更加自然。
Vein 中的惰值性能 (How to list big models cheaply: Vein vs SwiftData)
两周前我在 SwiftData:优化从建模开始 中讨论了“胖模型”给 SwiftData 列表带来的内存和性能压力,并尝试通过模型拆分减少一次查询真正加载的数据。Mia Köring 使用了我文章中的测试代码,展示了她开发的 Vein 持久化框架在字段级惰性加载下的表现。
Vein 采用了与 SwiftData Model 类似的声明方式,默认同样是属性预加载、关系惰性加载;不同之处在于,给某个属性标注 @LazyField 后,它会推迟到真正被访问时才去读取。因此在“只显示标题”的列表场景中,无需拆分模型也能取得不错的成绩。
让自动化自己证明它做对了 (Sad But True: Localized App Store Screenshots That Verify Themselves)
自动化被越来越多地应用到 App 开发的各个环节,但有时实践中的难点并非如何实现自动化,而是如何让它自己证明“我做对了”。
Wesley Matlock 在通过自动化方式为 App Store 生成多语言截图时,发现西班牙语截图悄悄回退成了英文,而整个测试流程依然通过。检查后发现,原因来自 Xcode 27 下 -testLanguage 没有可靠地应用到 App。Wesley 随后改用 simctl 设置 Simulator 的语言,并通过比较截图的 SHA-256,自动检查不同语言或不同页面是否意外生成了完全相同的图片。
Wesley 基于截图哈希的比较思路可以迁移到任何“把请求交给黑盒、再把产出照单全收”的流程里。
Transition or ContentTransition
transition 和 contentTransition 名字很像,但解决的是两类不同的问题:前者用于 View 被插入或移出视图层级时的动画,后者则用于 View 仍然存在、只是显示内容发生变化的情况。Stewart Lynch 通过条件视图、数字变化和 SF Symbols 等例子对两者进行了直观比较,并介绍了 .numericText、.interpolate、.symbolEffect 等常用的内容过渡效果。
在 SwiftUI 中,
transition很早就支持自定义,但这种能力始终没有延伸到 Sheet、导航切换等更高层级的场景。这些场景与contentTransition类似,目前仍主要依赖系统提供的有限几种过渡方式。期待未来 SwiftUI 能进一步打通这些不同层级的 transition 概念,或者至少为开发者提供更多自定义空间。
用 Scope 为视图状态划一条边界 (Abstracting SwiftUI state with scopes)
在 SwiftUI 中,如果视图中有多个 @State,我们可能会将它们抽象到一个 @Observable 中。但如果视图还依赖其他环境值或其他 Observable 实例呢?是否有一种方式,可以将来自不同数据源的状态组织到一起,同时减少视图对具体数据源的耦合?Mike Apurin 提出了 Scope 思路:通过他开发的 ScopedState 将多个状态源组织到统一的访问边界中,只向 View 暴露当前界面真正需要的状态和操作。View 无需了解这些状态实际来自哪里,也更容易在 Preview 和测试中替换数据来源。需要注意的是,这并不是试图创建另一套类似 TCA 或 MVVM 的状态管理模型,而是一种作用于 View 层级、对现有状态源进行组织和抽象的方式。
折叠之前:iPhone Duo 适配
iPhone Duo 的推出让消费者在惊叹设备形态变化的同时,也给开发者带来了新的挑战:如何利用更大的显示空间,如何让现有布局适应不同的设备形态,以及如何利用折叠、多显示区域等新的交互能力。上周,不少内容创作者都从不同角度给出了自己的建议。
苹果在发布当天放出了六个 Tech Talk:Design for iPhone Duo、Prepare your app、Raise the bar、Strike a pose with adaptive layouts、Leverage multiple displays and scenes、Build a great camera experience,并在 HIG 中增加了 Designing for iPhone Duo 一页。
Florian Schweizer 强调“不要与框架对抗”:不要从判断“是不是 iPhone Duo”开始,而应该问“这个视图拿到了多少空”。他还将这些原则以及 Duo 的新 API、Apple 开发者资料整理成了一个 SwiftUI iPhone Duo Skill,供 Coding Agent 在适配现有 App 时使用。
Lee young-jun 从现有 App 的实际适配出发,通过
NavigationSplitView、sidebarAdaptable、Device Hub 的 Resizing Mode 等方式,提前检查和改善 App 在 iPhone Duo 不同显示形态下的表现。Sagar Unagar 根据 Apple 最新的 HIG 和开发者资料,梳理了 iPhone Duo 在自适应布局、系统控件、折叠区域以及不同显示形态下的主要设计原则。
Jordan Morgan 总结了开发者首先需要关注的布局、系统栏、safe area、相机以及多显示区域等变化。
Artem Mirzabekian 介绍了新的
ArrangementView,开发者可以描述两组相关内容之间的关系,让 SwiftUI 根据可用空间和折叠区域自动选择合适的布局方式。
上面介绍的功能不少都只存在于 Xcode 27.1 中,想完整体验还需要一段时间。
工具
SwiftMusic:用 Swift 声明音乐
Norikazu Muramoto 开发的 SwiftMusic 是一套面向 Apple 平台开发者的声明式音乐创作框架。它借鉴了 SwiftUI 的设计理念,将 Music、Sound、body、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 端的实时编辑器 MusicPlaygournd 来承担这部分工作。
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 的开发者需要特别留意。


