Fatbobman's Swift Weekly #160

The Case of the Rapidly Disappearing Disk Space

Read on web →

My MacBook contains almost nothing but development tools and related data, and I’ve long maintained at least 500–600 GB of free space. For quite some time, I assumed its internal storage was more than sufficient. But coding agents have changed that. Last week, I found myself in the rather embarrassing situation of having my disk space rapidly exhausted—not once, but twice.

The first incident was caused by an agent continuously generating large amounts of data during development, while the directories involved were still included in Time Machine backups. As files were repeatedly created, modified, and deleted, the data blocks retained by local snapshots kept accumulating, eventually consuming a substantial amount of disk space. The problem wasn’t resolved until I excluded those directories from backups and cleared the existing local snapshots.

The second incident came down to a cleanup bug in a test script written by the agent itself. The script called mkdtempSync to create temporary directories but never included any corresponding cleanup operation. Every time the QA workflow ran, it unpacked an entire 5.7 GB Design repository into a new directory. As the tests ran repeatedly, enormous amounts of junk data quickly piled up on my disk.

Ironically, both problems were ultimately resolved with the help of an agent, even though agents had caused them in the first place. Compared with the relatively linear pace of human developers, agents tend to push development forward in a much more aggressive and parallelized manner. As a result, disk storage is becoming another significant bottleneck in this new workflow, alongside CPU and RAM. And Apple’s notoriously expensive storage upgrades certainly don’t help. I suspect quite a few developers shopping for a new Mac capable of handling today’s development workloads have found themselves wincing at the final price tag.

The AI “boom” has driven sharp increases in memory and flash storage prices over a short period, while new development workflows are simultaneously pushing developers toward ever more powerful hardware. The whole situation feels like a peculiar kind of arms race.

But rather than simply spending more money on larger SSDs, perhaps what we really need is to adapt more quickly to this shift in how we work: establish clearer, stricter boundaries for our agents’ execution environments, along with reliable resource management and cleanup practices. Otherwise, no amount of storage will be enough to withstand an agent’s unchecked, brute-force consumption.

Previous Issue|Newsletter Archive


This Week’s Sponsor

Arborist —— Git worktrees, made easy

A native Mac app that runs your Make, mise, npm, and just tasks on any branch, shows its pull request, and resumes Claude Code, Codex, or Copilot chats where they happened. New in 1.4: spot stale worktrees and clear them out in one step. 14-day free trial, then $39 once.

Try Arborist →


Recent Recommendations

Interesting Ways of Using Swift Optionals

Optional is one of Swift’s most fundamental, frequently used, and distinctive types. Swift provides a rich set of APIs and syntactic conveniences for working with it. But beyond if let, guard let, and optional chaining, there’s still plenty worth exploring. John Sundell introduces several interesting and less common techniques: using take() to combine two operations, extracting the wrapped value while resetting the variable to nil; switching over an optional enum to handle nil alongside its various cases; building chains of data transformations with map and flatMap; and extending Optional with generic constraints and KeyPaths to encapsulate reusable conditional logic.

When I first started learning Swift, I was constantly amazed by all the little tricks Optional had to offer. Perhaps you don’t write much code by hand anymore, but these techniques can still remind you of what makes the Swift language itself so appealing.

SwiftUI Performance: Find Redraws in Xcode Instruments

Antoine van der Lee uses a SwiftUI app with deliberately introduced performance issues to demonstrate how to identify bottlenecks using the SwiftUI template in Xcode Instruments. He shows how to locate views with high cumulative execution time in the View Body Updates hierarchy, examine the duration and Root Causes of individual body evaluations in the details table, and use the causal graph to trace the relationship between state changes and view updates.

Antoine reminds readers that analyzing SwiftUI performance requires looking beyond the execution time of a single body evaluation. Update frequency and the underlying triggers must also be considered to accurately identify the source of a problem.


iOS 27: Now Playing Framework

For years, iOS has spread media metadata, playback state, and remote control capabilities across multiple APIs. The new Now Playing framework in iOS 27 brings these capabilities together under a unified media session model. Anton Gubarenko demonstrates how to describe media content with MediaSessionRepresentable, provide playback state snapshots through MediaPlaybackSnapshot, and declare the playback operations available to the system using MediaCommand.

Beyond simplifying system integration for local media playback, the framework also supports Remote Media Sessions. With App Extensions and push notifications, apps can display the playback state of external speakers, TVs, and other devices on the Lock Screen and in Control Center, and respond to user controls even when the main app isn’t running.


Positioning SwiftUI Views Around the Fold on iPhone Duo

When adapting an app for iPhone Duo, keeping views looking natural and stable as the device folds and unfolds presents a new challenge. Natalia Panferova explores an important idea: adaptation isn’t just about avoiding hardware constraints; it’s also about maintaining visual continuity as the device changes shape.

Natalia explains how to use the .includeInactive option of GeometryProxy.reservedRegions(kind:options:) to obtain information about fold regions that aren’t currently active, allowing layouts to prepare for the folded state in advance. By determining the grid’s column count ahead of time and dynamically adjusting column widths and spacing during folding, content can remain distributed on both sides of the crease without large shifts caused by changes in column count.


SwiftUI Text Interpolation Beyond Plain Strings

Although Text("Hello, \(name)") looks similar to ordinary String interpolation, it actually relies on a set of specialized interpolation methods provided by LocalizedStringKey. These methods can handle more than strings: they also support numbers, dates, Text, Image, and other types, formatting content according to regional language and formatting conventions.

Stewart Lynch explores several techniques for Text interpolation: formatting currencies, percentages, and dates with FormatStyle; creating automatically updating relative times and countdowns with style: and timerInterval:; embedding differently styled Text values or SF Symbols within a single sentence; and using AttributedString when text styling needs to be determined at runtime. Compared with concatenating multiple Text values, interpolation also preserves the complete localizable sentence, allowing translators to rearrange content according to the conventions of each language rather than being constrained by the original concatenation order. This is also an important reason why the Text concatenation operator (+) was deprecated starting in iOS 26.


The Best API Change Is the One Nobody Notices

Adding new capabilities to a Swift framework can break compatibility when it involves introducing generic parameters or changing protocol requirements, potentially forcing existing users to migrate their code. Michael Long uses the addition of scene-scoped custom container support to the Factory dependency injection framework to demonstrate how internal design changes can extend functionality while leaving the public API largely unchanged. To avoid modifying the generic signatures of existing property wrappers, he moves the differences between container types out of the type system and into the data layer, using a single Environment Key and an ObjectIdentifier-based registry to manage multiple container instances. When no corresponding scene container is found, the implementation falls back to the original shared instance.

The article also explains how computed properties can preserve the existing Environment API, and how DynamicProperty.update() allows @InjectedContainer to access scene-specific containers within SwiftUI views while maintaining its original behavior in ordinary classes.

Michael’s approach raises an important question for framework authors: adding new capabilities doesn’t necessarily have to come at the cost of breaking API changes. The key is finding the right internal abstraction boundaries and balancing type safety, backward compatibility, SwiftUI’s environment mechanisms, and API simplicity.

Run Multiple Claude Code Agents on the Same Xcode Project

Having multiple agents work on the same Xcode project in parallel is becoming increasingly common. But isolating source code with Git worktrees doesn’t mean the build and testing environments are isolated as well. For example, two agents might both report successful builds, yet because they share the same Simulator, the app installed by the second agent overwrites the first. An agent could then unknowingly run and verify an app built by another agent, undermining the reliability of its test results.

Amarildo Lucas analyzes the causes of this problem and discusses how separate DerivedData directories, Simulators, and build configurations can reduce interference between parallel agents. Amarildo reminds developers that multi-agent collaboration requires isolating more than code: build artifacts, runtime environments, and verification processes must also be kept separate.


Applying for an ICP Filing Exemption for Apps on China’s App Store

For developers hoping to distribute their apps through the App Store in mainland China, ICP filing requirements can be a significant hurdle, particularly for overseas indie developers who lack servers in mainland China or the infrastructure needed for filing. However, Apple offers an exemption application process for apps that primarily run locally and don’t depend on self-hosted servers.

Junyu Fang shares his complete experience applying for an ICP filing exemption. The process involved submitting a request through the Apple Developer contact page, preparing a declaration letter containing developer information, the App ID, and the required statements, uploading the PDF documentation, and ultimately receiving approval—all within approximately six days. The article also provides a declaration letter template and step-by-step instructions. Of course, this exemption isn’t automatically available to every offline app. Apple’s approval applies only to the specific app covered by the request and does not exempt developers from other applicable legal and regulatory requirements.

Tools

SwiftyDMG: A Native SwiftUI App for Creating DMG Images

For developers distributing apps outside the Mac App Store, creating a well-designed DMG is another detail worth polishing before release. Chocoford brings this workflow into a native SwiftUI interface with SwiftyDMG. Simply drag in an .app file, then use the live preview to adjust the positions of the app icon and Applications folder shortcut, configure the window size, customize the background and disk icon, and generate the installation image. It also provides a Finder context menu service and detailed build logs for quick packaging and troubleshooting.


PreviewMD: Quick Look and Markdown Reader for macOS

Markdown has become one of the primary formats for developer documentation, yet macOS Quick Look still lacks built-in support for fully rendered Markdown previews. Adam Jesionkiewicz fills this gap with PreviewMD: select a file in Finder and press the Space bar to preview rendered headings, tables, task lists, code, and local images. It also includes a standalone reader that can open existing folders, browse documents, render Mermaid diagrams, follow Obsidian bidirectional links, and switch between preview, split, and editing modes.


szt: An Ultra-Fast Disk Usage Analyzer Written in Swift

Tommi Urtti developed szt (sizetree), an open-source command-line tool written in Swift that quickly scans directories and displays disk usage as a colorful tree. It prioritizes expanding directories that consume the most space while collapsing less significant entries, helping developers quickly identify where their storage is going.

Another highlight of szt is its performance. It uses macOS’s getattrlistbulk to retrieve file metadata in batches and scans directories in parallel using multiple threads. In the author’s benchmark, scanning a directory containing 667,000 files totaling 123 GB took just 2.4 seconds, compared with approximately 17 seconds for the traditional du command. When I encountered an unusually rapid loss of free disk space last week, szt helped me speed up the investigation.


Thanks for reading Fatbobman’s Swift Weekly! This post is public so feel free to share it.


快速消失的硬盘容量

网页版

我的 MacBook 上几乎只保存与开发有关的工具和数据,长期空闲容量至少保持在 500~600 GB,因此在相当长的一段时间里,我都认为内置的硬盘容量绰绰有余。但 Agent 改变了这种情况——上周,我接连遭遇了两次硬盘空间在短时间内被迅速耗尽的窘境。

第一次,是因为 Agent 在开发过程中不断产生大量数据,而相关目录仍被包含在 Time Machine 的备份范围内。随着文件持续创建、修改和删除,本地快照保留的数据块不断累积,最终占用了大量磁盘空间。直到我将这些目录加入备份排除列表,并清理掉已有的本地快照后,问题才得以解决。

第二次,则是 Agent 自己编写的测试脚本存在清理漏洞:调用 mkdtempSync 创建临时目录后,却没有对应的清理操作。每执行一次 QA 流程,就会完整解包一份 5.7 GB 的 Design 仓库。随着测试不断重复,硬盘里很快堆积起海量的垃圾数据。

有意思的是,两次问题最终都是在 Agent 的协助下解决的,但起因却也都是它。相较于人类开发者习惯的线性工作节奏,Agent 往往会以更加激进、并行化的方式推进开发。这使得除了 CPU 和 RAM,硬盘存储也开始成为这种全新工作流下不可忽视的瓶颈。而苹果「金子级」的存储定价又是出了名的昂贵。相信不少开发者在选购适合当下开发需求的新设备时,看到最终的配置单价格,都会忍不住倒吸一口凉气。

AI 的“繁荣”让内存与闪存的价格在短时间内大幅提升,而全新的开发方式又倒逼着开发者不断升级硬件,一切仿佛陷入了一种奇妙的“军备竞赛”。

不过,除了花钱购买更大的存储容量,我们或许更需要尽快适应这种工作范式的转变:为 Agent 划定更加清晰、严格的环境边界,建立可靠的资源管理与清理规范。否则,再大的硬盘容量,也经不起 Agent 毫无节制的暴力消耗。

前一期内容|全部周报列表

如果您发现这份周报或我的博客对您有所帮助,可以考虑通过 Buy Me a Coffee 支持我的创作。

本期赞助

Arborist —— 轻松管理 Git worktree

一款原生 Mac 应用:在任意分支上运行 Make、mise、npm 和 just 任务,查看对应的 Pull Request,并在对话发生的位置恢复 Claude Code、Codex 或 Copilot 会话。1.4 新增:一步找出并清理过期的 worktree。14 天免费试用,之后一次买断 39 美元。

试用 Arborist →


近期推荐

Swift Optional 的进阶妙用 (Interesting ways of using Swift optionals)

Optional 是 Swift 中最基础、最常用,也最具特色的类型之一。围绕它,Swift 提供了丰富的 API 和语法糖。但除了 if let、guard let 和可选链之外,Optional 还有不少值得挖掘的能力。⁠John Sundell 在本文中介绍了几种有趣且不太常见的用法:take() 将两个操作合二为一,在取出包装值的同时将变量重置为 nil;对 Optional Enum 进行 switch,将 nil 与各个枚举 case 放在一起处理;通过 map、flatMap 构建连续的数据转换链;以及利用泛型约束和 KeyPath 扩展 Optional,封装可复用的判断逻辑。

初学 Swift 时,我总会惊叹于 Optional 提供的各种小技巧。或许如今你已经不再经常亲手编写代码,但这些技巧依然能让你重温 Swift 语言本身的魅力。

用 Instruments 排查 SwiftUI 视图重绘 (SwiftUI Performance: Find Redraws in Xcode Instruments)

⁠Antoine van der Lee 以一个刻意制造性能问题的 SwiftUI 应用为例,展示了如何利用 Xcode Instruments 的 SwiftUI 模板定位性能瓶颈:通过 View Body Updates 的层级视图找出累计耗时较高的视图,借助更新明细表查看每次 body 执行的耗时及 Root Causes,再利用因果关系图追踪状态变化与视图更新之间的联系。

Antoine 提醒读者,分析 SwiftUI 性能时,不能只关注单次 body 执行的耗时,还需要结合更新频率与触发原因,才能更准确地判断问题所在。


认识 iOS 27 全新 Now Playing 框架 (iOS 27: Now Playing Framework)

长期以来,iOS 的媒体元数据、播放状态和远程控制能力分散在不同的 API 中。iOS 27 引入的 Now Playing 框架,将这些能力整合到统一的媒体会话模型中。⁠Anton Gubarenko 展示了如何通过 MediaSessionRepresentable 描述媒体内容、使用 MediaPlaybackSnapshot 提供播放状态快照,并通过 MediaCommand 声明系统可以执行的播放操作。

除了简化本地媒体播放的系统集成,新框架还支持远程媒体会话(Remote Media Session)。借助 App Extension 和推送通知,即使主应用没有运行,也能在锁屏和控制中心呈现外部扬声器、电视等设备的播放状态,并响应用户的控制操作。


适配 iPhone Duo:折叠屏上的 SwiftUI 布局探索 (Positioning SwiftUI views around the fold on iPhone Duo)

在适配 iPhone Duo 时,如何让视图在设备折叠与展开过程中保持自然、稳定的呈现,是开发者面临的新挑战。⁠Natalia Panferova 在本文中提出了一个重要思路:适配不只是避开硬件限制,还需要考虑设备形态变化时的视觉连续性。

Natalia 介绍了如何利用 GeometryProxy.reservedRegions(kind:options:) 的 .includeInactive 选项,提前获取尚未激活的折叠区域,为折叠状态做好布局准备。通过预先确定网格列数,并在折叠过程中动态调整列宽与间距,让内容始终分布在折痕两侧,避免因列数变化导致内容位置大幅移动。


不止于字符串:SwiftUI Text 插值技巧 (SwiftUI Text Interpolation Beyond Plain Strings)

尽管 Text("Hello, \(name)") 看起来与 String 的插值语法类似,但背后使用的实际上是 LocalizedStringKey 提供的一系列专用插值方法。它不仅能插入字符串,还能直接处理数字、日期、Text、Image 等类型,并根据不同地区的语言和格式习惯呈现内容。

⁠Stewart Lynch 介绍了 Text 插值的多种用法:通过 FormatStyle 格式化货币、百分比和日期;使用 style: 与 timerInterval: 实现自动更新的相对时间和倒计时;在同一句话中嵌入不同样式的 Text 或 SF Symbols;以及在需要根据运行时数据设置文本样式时使用 AttributedString。另外,相比直接拼接多个 Text,插值可以保留完整的本地化语句,让翻译人员根据语言习惯重新排列内容,而不受原始拼接顺序的限制。这也是 Text 的 + 拼接操作符从 iOS 26 开始被弃用的重要原因。


无感演进:框架 API 变更的设计艺术 (The Best API Change Is the One Nobody Notices)

为 Swift 框架增加新能力,如果涉及增加泛型参数、修改协议要求,往往会破坏兼容性,甚至要求现有用户迁移代码。⁠Michael Long 以依赖注入框架 Factory 新增 Scene 级自定义容器支持为例,展示了如何通过调整内部设计,在基本保持公开 API 不变的情况下完成扩展。为了避免修改现有属性包装器的泛型签名,他选择将不同容器类型的差异从类型系统转移到数据层,通过单一 Environment Key 和基于 ObjectIdentifier 的注册表管理多个容器实例,并在未找到对应 Scene 容器时回退到原有的共享实例。

文章还介绍了如何通过计算属性保留既有的 Environment API,以及利用 DynamicProperty.update() 让 @InjectedContainer 在 SwiftUI View 中读取 Scene 容器,同时维持普通类中的原有行为。

Michael 的实践展示了一个值得框架作者思考的问题:新能力不一定需要以破坏性 API 变更为代价,关键在于找到合适的内部抽象边界,并在类型安全、向后兼容、SwiftUI 环境机制和 API 简洁性之间做出权衡。

在同一 Xcode 项目中安全并行多个 Claude Code Agent (Run Multiple Claude Code Agents on the Same Xcode Project)

让多个 Agent 并行开发同一个 Xcode 项目正变得越来越普遍。但即使通过 Git Worktree 隔离了源代码,也不意味着构建与测试环境同样实现了隔离。例如,两个 Agent 都报告构建成功,但由于共享同一个 Simulator,后安装的应用覆盖了先前的版本。这意味着 Agent 可能在不知情的情况下,运行并验证了另一个 Agent 构建的应用,导致测试结果失去可信度。

⁠Amarildo Lucas 分析了这一问题的成因,并讨论了如何通过独立的 DerivedData、Simulator 和构建配置减少并行 Agent 之间的相互干扰。Amarildo 提醒开发者:多 Agent 协作需要隔离的不只是代码,还包括构建产物、运行环境与验证过程。


Apple 中国区 App Store ICP 备案例外申请

对于希望在中国大陆 App Store 上架应用的开发者来说,ICP 备案可能是一道不小的门槛,尤其是缺乏中国大陆服务器及相关备案条件的海外独立开发者。不过,对于主要在本地运行、不依赖自建服务器的 App,Apple 提供了一条例外申请途径。

⁠方君宇 分享了为自己申请 ICP 备案例外的完整经历。从通过 Apple Developer 联系页面提交申请,到准备包含开发者信息、App ID 和相关声明的信函,再到上传 PDF 材料并获得批准,整个流程耗时约 6 天。文章还提供了声明信函模板及各个环节的操作说明。当然,这项例外申请并非自动适用于所有离线 App,Apple 的批准也仅针对申请的特定 App,也不代表免除其他适用的法律法规要求。

工具

SwiftyDMG:原生 SwiftUI 的 DMG 镜像制作器

对于在 Mac App Store 之外分发应用的开发者,制作一个布局得体的 DMG,也是发布前值得打磨的一环。⁠Chocoford 开发的 SwiftyDMG 将这套工作流带入了原生 SwiftUI 界面:拖入 .app,便可在实时预览中调整应用图标与 Applications 文件夹快捷方式的位置,设置窗口尺寸、自定义背景和磁盘图标,再生成安装镜像。它还提供 Finder 右键服务和详细的构建日志,方便快速打包与排查问题。


PreviewMD:macOS 上的 Markdown 快速预览与阅读器

Markdown 已经成为开发者文档的主要格式之一,但遗憾的是,macOS 的 Quick Look 至今没有内置完整的 Markdown 渲染预览能力。⁠Adam Jesionkiewicz 开发的 PreviewMD 补上了这个缺口:在访达中选中文件、按下空格,就能预览排版后的标题、表格、任务列表、代码和本地图片。它还附带独立阅读应用,可以直接打开现有文件夹,浏览文档、渲染 Mermaid 图表、跳转 Obsidian 双向链接,并在预览、分栏和编辑模式之间切换。


szt:Swift 编写的超高速磁盘分析命令行工具

Tommi Urtti 开发的 szt(sizetree)是一款使用 Swift 开发的开源命令行工具,可以快速扫描指定目录,并以彩色树状结构展示磁盘空间的占用情况。它会优先展开占用较大的目录,将次要内容折叠起来,让开发者能够迅速定位空间消耗的主要来源。

szt 的另一个亮点是性能。它通过 macOS 的 getattrlistbulk 批量获取文件元数据,并利用多线程并行扫描。在作者的测试中,扫描包含 66.7 万个文件、总计 123 GB 的目录仅需 2.4 秒,而传统 du 命令需要约 17 秒。在上周我碰到的磁盘空间异常快速消耗的问题中,szt 帮我加快了排查过程。

参与讨论