Fatbobman's Swift Weekly #159

When Apps Are No Longer Tied to a Specific Operating System

Read on web →

This week’s issue features three intriguing projects: Cutefish, SimRun, and Mac Android Subsystem. They take different technical approaches. One provides a runtime environment by implementing compatible APIs and frameworks, another runs apps built for a simulator directly, and the third hides an entire virtualized operating system in the background. Yet they all share the same goal: allowing apps originally built for different operating systems to coexist naturally on the same desktop.

Running apps across platforms is nothing new. From Wine to various virtual machines and emulators, developers have been working toward this goal for decades. But simply getting an app to run is no longer enough. The aim is increasingly to make users forget which platform an app originally belonged to. Independent windows, Dock integration, Spotlight, clipboard sharing, system notifications… As these capabilities come together, the underlying runtime fades into the background, and the boundaries between an app and its host operating system begin to blur.

AI is also accelerating this process. In the past, reverse-engineering system APIs, recreating framework behavior, or porting complex application runtimes required enormous amounts of time and effort. Today, Coding Agents can take on a substantial portion of the exploration, implementation, and verification work, giving individual developers and small teams opportunities to tackle projects that were previously beyond their reach. The cost of crossing these boundaries is coming down.

This raises an interesting question: if apps become less dependent on specific operating systems, how will we choose our hardware in the future? In the past, a single exclusive app or an irreplaceable software ecosystem could be enough to determine which device someone bought. But as more apps become capable of crossing platform boundaries, hardware design, performance, price, the operating system experience, and even brand loyalty or personal preference may carry more weight in purchasing decisions.

Interestingly, this doesn’t necessarily mean competition between platforms will weaken. Quite the opposite: platforms that can integrate apps from different ecosystems more naturally and reliably may gain a new kind of appeal. The focus of competition may gradually shift from “Which apps do I have exclusively?” to “What kind of experience can I offer for these apps?”

Perhaps one day, when choosing a device, we won’t have to start by asking which apps it can run. Instead, we’ll have more freedom to ask how we actually want to use it.

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.

📢 View Sponsorship Options

Enjoyed this issue? Buy me a coffee ☕️


Recent Recommendations

Running Mac apps on Cutefish

Running Windows applications on Linux has become commonplace, but running unmodified macOS applications directly on Linux still sounds almost unbelievable. Cutefish is working toward that goal. Rather than using virtualization or instruction emulation, it has developed a compatibility layer called Reef that reimplements the Mach-O loader, Objective-C Runtime, and frameworks such as Foundation, AppKit, Core Graphics, and SwiftUI, allowing Mac applications to run directly in a Linux desktop environment. The article not only demonstrates the current state of compatibility but also openly discusses performance issues, such as rebuilding the entire view tree when local state changes and redundant drawing during scrolling.

Swift continues to expand its cross-platform capabilities, but many applications written in Swift still depend heavily on frameworks in Apple’s ecosystem, making them difficult to port directly to other platforms. Reef offers another possibility: instead of modifying existing applications, give other platforms the ability to run them.

Every iOS Haptic Pattern, With Code

iOS provides several APIs for haptic feedback, but their capabilities and intended use cases differ. Alok Singh brings together UIKit’s Feedback Generators, SwiftUI’s .sensoryFeedback, and Core Haptics, with code examples covering system-provided feedback, custom haptic patterns, dynamic parameters, and AHAP files.

Since haptic feedback cannot be experienced in the Simulator, Alok also developed Haptics Explorer, allowing developers to compare different haptic patterns directly on an iPhone and obtain the corresponding Swift code.


macOS/iOS Audio Ducking and Smooth Mixing: AVFoundation Pipelines and Concurrency Pitfalls

When background music and voice narration need to play simultaneously, simply adjusting the music volume when speech starts and stops can lead to abrupt changes, or even a repeated “pumping effect” during natural pauses in speech. Snow Wu explains how asymmetric volume fades can quickly lower the background music and restore it more gradually, while Generation Tokens and delay windows help prevent frequent volume changes caused by natural pauses. More valuable than the code itself is the design approach: changes in audio state should align with human auditory perception, while asynchronous events need to be coordinated before they can produce a stable playback state.


A Dismantled NSHostingView Can Still Create Its View Again

When combining SwiftUI and AppKit, does calling dismantleNSView on an NSViewRepresentable guarantee that its internal views will never be created again? Wang Wei (Onevcat) discovered a counterintuitive behavior: even after the outer Representable has entered the dismantling process, a nested NSHostingView may still invoke its child view’s makeNSView during a subsequent layout transaction. As a result, a terminal Surface that had already been transferred to the Canvas was reclaimed by an invisible host.

The article documents the entire investigation, from bisecting versions and uncovering a subtle connection between an .overlay badge and the issue’s reproducibility, to building a minimal reproduction and confirming the nested Hosting View lifecycle problem. Ultimately, the author not only explicitly removed the inner NSHostingView in dismantleNSView, but also improved the ownership rules for the shared Surface so it could be recovered from a host that was no longer visible.

Another valuable aspect of this article is how it demonstrates an effective way to use AI Agents for complex debugging tasks. Rather than merely modifying code, the Agent builds a chain of evidence through repeated experiments, while the developer asks why at critical moments, avoiding the temptation to end the investigation as soon as the symptoms disappear.

How I built Live Voice: one tool contract, six realtime voice providers

Integrating real-time voice AI into an application involves more than handling audio streams and model responses. Another challenge is allowing the model to interact with the application safely and reliably. Apoorv Darshan shares his experience building Live Voice for his open-source app Fud AI (built with SwiftUI on iOS and Compose on Android). By defining 25 application actions through a unified JSON tool contract, he ensures that iOS, Android, and a Cloudflare Worker follow the same interface, while separate adapters handle differences in audio formats, tool calls, and visual input across services such as Gemini Live, OpenAI Realtime, xAI, and Qwen.

Apoorv categorizes tools into read, navigation, write, and destructive operations. Writes come with an eight-second Undo window, while destructive operations require user confirmation. By default, ordinary writes also require confirmation.


Why Tuist should build the harness on top of compute

Just a few months ago, Tuist co-founder Pedro Piñera expressed reservations about the trend toward fully remote development in The remote development mirage. But after spending more time with Amp, he began reconsidering that position. As Coding Agents become capable of independently completing more coding tasks, developers may no longer need to sit in front of a computer and work directly in an editor. Mobile devices, voice interfaces, and even Live Activities could become new entry points for development.

In this article, Pedro argues that as compute becomes increasingly commoditized, Tuist should build its competitive differentiation around the coding harness and developer experience layered on top of it. In his earlier article, The automation graph agents need, he explored how Agents could determine subsequent testing and verification tasks based on code changes. Now, he wants to combine those capabilities with remote compute resources, moving development tools from passively executing commands toward proactively understanding projects, organizing tasks, and verifying results.

When code editing is no longer the center of software development, do we really need a smarter IDE, or an entirely new development environment?

My Claude Code loads 39.6k tokens before I type anything — here’s where they go

Where does Claude Code’s context overhead come from? The /context all command lets you inspect the initial context usage before entering a single prompt. Beyond system prompts and tool definitions, CLAUDE.md, Rules, Skills, and custom Agents can also account for a substantial portion of that context, while MCP tools that use deferred loading are not included in the initial usage.

Hao Liu explains how to use /skill-doctor and /doctor prompt-audit to identify rarely used Skills, duplicate rules, and outdated instructions. He also suggests improvements such as loading rules based on file paths and trimming CLAUDE.md. The author reminds readers that context usage is not the same as the actual cost of each request, since Prompt Caching can reduce the cost of repeated content. Rather than simplifying configurations based on assumptions, it is better to measure your own environment first and then decide what is truly worth removing.

Tools

SimRun: Run iOS Apps in Independent Mac Windows

Developed by Cyandev, SimRun is an intriguing open-source experimental project. Without launching a complete simulated device or adding Mac Catalyst support to an application, users can run iOS apps built by Xcode for the iOS Simulator directly in independent macOS windows. For developers who frequently iterate on SwiftUI and UIKit interfaces, this offers another way to assist with development.

The project also provides a command-line interface, screenshots, view hierarchy inspection, and an experimental JavaScript automation API. It supports running applications without displaying a window, making it easier to write inspection scripts or let AI Coding Agents interact with applications. Note that SimRun requires an Apple Silicon Mac, Xcode, and the Simulator runtime, and that System Integrity Protection (SIP) must be disabled.


Mac Android Subsystem: Bringing Android Apps to the Mac Desktop

Somewhat similar to the project above, Mac Android Subsystem (MAS), developed by Harkhowalia, aims to give Android apps an experience closer to native Mac applications. Users can install an APK by double-clicking it, and each app gets its own window and Dock icon. Apps can be found through Spotlight, switched to with ⌘-Tab, and integrated with Notification Center and a bidirectional clipboard.

MAS does not run Android applications directly. Instead, it uses LineageOS, QEMU, and Apple’s Hypervisor Framework to run a complete Android environment in the background, then uses scrcpy to display individual applications in separate Mac windows. The project is still in an early pre-release stage, requires an Apple Silicon Mac running macOS 15 or later, and does not include the Google Play Store or Google services.


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


当应用不再属于特定操作系统

网页版

本期周报中有三个颇有意思的项目:Cutefish、SimRun 和 Mac Android Subsystem。它们采用了不同的技术路线,有的通过兼容 API 和框架来提供运行环境,有的直接运行针对模拟器构建的应用,还有的将完整的虚拟系统隐藏在后台。但它们都在尝试做同一件事:让原本属于不同操作系统的应用,能够自然地出现在同一张桌面上。

跨平台运行当然不是什么新鲜事。从 Wine 到各种虚拟机、模拟器,开发者已经为此努力了几十年。不过,开发者不再仅仅满足于让应用「能够运行」,而是希望用户不必在意应用原本属于哪个平台。独立窗口、Dock、Spotlight、剪贴板、系统通知……当这些能力逐渐融合,运行环境便开始退居幕后,应用与宿主系统之间的边界也随之变得模糊。

AI 也正在加快这一进程。过去,无论是逆向分析系统 API、复刻框架行为,还是移植复杂的应用运行环境,都需要投入大量时间和人力。如今,Coding Agent 可以承担其中相当一部分探索、实现与验证工作,让个人开发者和小团队也有机会尝试过去难以负担的项目,打破这些边界的成本正在下降。

一个问题也逐渐呈现:如果应用不再那么依赖特定的操作系统,我们未来会如何选择硬件?过去,一款独占应用、一个不可替代的软件生态,可能就足以决定用户购买哪台设备。而当越来越多的应用可以跨越系统边界,硬件本身的设计、性能、价格、系统体验,甚至品牌忠诚度和个人喜好,或许会在购买决策中占据更大的分量。

有趣的是,这未必意味着平台之间的竞争会减弱。恰恰相反,谁能让来自不同生态的应用更自然、更稳定地融入自己的系统,谁就可能拥有新的吸引力。竞争的重点或许会逐渐从「我独占哪些应用」,转向「我能为这些应用提供怎样的使用体验」。

也许未来选择一台设备时,我们不必首先考虑它能运行哪些应用,而是可以更多地考虑,我们究竟喜欢怎样使用它。

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

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

近期推荐

在 Linux 上运行原生 Mac 应用 (Running Mac apps on Cutefish)

在 Linux 上运行 Windows 应用已经稀松平常,但让未经修改的 macOS 应用直接运行在 Linux 上,听起来则有些不可思议。⁠Cutefish 正在尝试实现这一目标。它没有采用虚拟机或指令模拟,而是通过一套名为 Reef 的兼容层,重新实现 Mach-O 加载器、Objective-C Runtime,以及 Foundation、AppKit、Core Graphics、SwiftUI 等框架,让 Mac 应用能够直接在 Linux 桌面环境中运行。本文不仅展示了当前的兼容成果,还坦诚讨论了性能问题:例如,局部状态变化时重建整棵视图树,以及滚动过程中的重复绘制。

Swift 正在不断拓展其跨平台能力,但大量使用 Swift 开发的应用仍然深度依赖苹果生态中的各类框架,难以直接移植到其他平台。Reef 则提供了另一种可能:不改变现有应用,而是让其他平台具备运行这些应用的能力。

iOS 触觉反馈完整指南 (Every iOS Haptic Pattern, With Code)

iOS 提供了多种触觉反馈 API,但不同机制之间的能力与适用场景并不完全相同。⁠Alok Singh 汇总了 UIKit 的 Feedback Generator、SwiftUI 的 .sensoryFeedback,以及 Core Haptics 的使用方式,并提供了涵盖系统预设反馈、自定义触觉模式、动态参数和 AHAP 文件的代码示例。

由于触觉反馈无法通过模拟器体验,Alok 还开发了 Haptics Explorer,让开发者能够直接在 iPhone 上比较不同触觉模式,并获取相应的 Swift 代码。


AVFoundation 音频闪避与平滑混音工程实践

在背景音乐与语音旁白需要同时播放的场景中,如果只是在语音开始和结束时直接调整背景音乐音量,很容易出现突兀的音量变化,甚至在语音自然停顿时产生反复起伏的「抽吸效应」。Snow Wu 介绍了如何通过非对称音量渐变,让背景音乐快速降低、缓慢恢复,并利用 Generation Token 与延迟窗口避免语音自然停顿引起的频繁音量变化。相较于代码,本文给出的设计思路更值得借鉴:音频状态变化需要符合人的听觉感知,而异步事件需要经过协调,才能转换成稳定的播放状态。


被拆掉的 NSHostingView 还会再建一次 View

在 SwiftUI 与 AppKit 混合开发中,NSViewRepresentable 的 dismantleNSView 已经被调用,是否意味着内部的视图不会再被创建?⁠王巍(Onevcat) 发现了一个反直觉的现象:外层 Representable 已经进入拆除流程,嵌套其中的 NSHostingView 却仍然可能在后续布局事务中重新调用子视图的 makeNSView,导致原本已经转移到 Canvas 的终端 Surface 被一个不可见的宿主重新夺走。

文章完整记录了从版本二分、发现 .overlay 角标与问题复现的隐蔽关联,到构建最小复现并确认嵌套 Hosting View 生命周期问题的过程。最终,作者不仅在 dismantleNSView 中主动移除内部的 NSHostingView,还改进了共享 Surface 的所有权规则,使其能够从不再显示的宿主中恢复。

本文的另一层价值在于它展示了 AI Agent 在复杂调试任务中的一种有效使用方式。Agent 不只是修改代码,而是通过反复实验建立证据链;开发者则在关键节点追问原因,避免在症状消失后过早结束调查。

一套工具契约,接入多种实时语音模型 (How I built Live Voice: one tool contract, six realtime voice providers)

在应用中接入实时语音 AI,难点不仅在于处理音频流和模型响应,还在于如何让模型安全、可靠地操作应用。⁠Apoorv Darshan 分享了他为开源应用 Fud AI(iOS 端基于 SwiftUI,Android 端基于 Compose)开发 Live Voice 功能的实践:通过统一的 JSON 工具契约定义 25 种应用操作,让 iOS、Android 和 Cloudflare Worker 遵循相同的接口,并使用独立的 Adapter 处理 Gemini Live、OpenAI Realtime、xAI 和 Qwen 等服务在音频格式、工具调用和视觉输入方面的差异。

Apoorv 将工具分为读取、导航、写入和破坏性操作,写入后提供 8 秒撤销窗口,破坏性操作则必须经过用户确认;默认设置下,普通写入同样需要确认。


当开发工具不再围绕编辑器 (Why Tuist should build the harness on top of compute)

几个月前,Tuist 联合创始人 ⁠Pedro Piñera 还在 ⁠The remote development mirage 中对全面转向远程开发的趋势持保留态度。但随着对 Amp 的深入使用,他开始重新审视这一判断。当 Coding Agent 可以独立完成越来越多的编码任务,开发者或许不再需要始终坐在电脑前,面对编辑器完成工作。移动设备、语音交互,甚至 Live Activities,都可能成为新的开发入口。

在本文中,Pedro 提出,随着 Compute 逐渐商品化,Tuist 应该将差异化竞争力建立在 Compute 之上的 Coding Harness 与开发体验中。他此前在 ⁠The automation graph agents need 中讨论过如何让 Agent 根据代码变更确定后续的测试与验证任务,而如今,他希望将这些能力与远程计算资源结合,让开发工具从被动执行命令,转向主动理解项目、组织任务并验证结果。

当代码编辑不再是开发活动的中心,我们需要的究竟是一款更智能的 IDE,还是一种全新的开发工作环境?

还没开始对话,Claude Code 就占用了 39.6k Token

Claude Code 的上下文开销究竟来自哪里?通过 /context all 指令,可以查看尚未输入任何提示词时的初始上下文占用。除了系统提示词和工具定义,CLAUDE.md、Rules、Skills 和自定义 Agents 也可能占据相当大的比例,而采用延迟加载的 MCP 工具并不计入初始占用。

Hao Liu 介绍了如何利用 /skill-doctor 和 /doctor prompt-audit 检查不常使用的 Skills、重复规则及过时指令,并提出按文件路径加载规则、精简 CLAUDE.md 等优化方法。作者也提醒读者,上下文占用并不等于每轮请求的实际费用,Prompt Caching 能够降低重复内容的计费成本,与其凭印象精简配置,不如先测量自己的环境,再决定哪些内容真正值得清理。

工具

SimRun:让 iOS 应用在独立的 Mac 窗口中运行

由 ⁠Cyandev 开发的 SimRun 是一个颇有意思的开源实验项目:无需启动完整的模拟设备,也无需为应用增加 Mac Catalyst 适配,使用者可以直接在 macOS 上以独立窗口运行 Xcode 为 iOS 模拟器编译的应用。对于需要反复调整 SwiftUI、UIKit 界面的开发者,这提供了另一种开发辅助方式。

项目还提供命令行、截图、视图层级检查,以及实验性的 JavaScript 自动化接口,并支持在不显示窗口的情况下运行应用,方便编写检查脚本或让 AI 编程代理操作应用。需要注意,SimRun 依赖 Apple 芯片 Mac、Xcode 与模拟器运行时,并要求关闭系统完整性保护(SIP)。


Mac Android Subsystem:让 Android 应用融入 Mac 桌面

与上面的项目有些神似,由 ⁠Harkhowalia 开发的 Mac Android Subsystem(MAS)尝试让 Android 应用拥有接近 Mac 应用的使用方式:双击 APK 即可安装,每个应用都有独立窗口和 Dock 图标,能够通过 Spotlight 搜索、使用 ⌘-Tab 切换,还支持通知中心和双向剪贴板。

MAS 并不是直接运行 Android 应用,而是基于 LineageOS、QEMU 和 Apple Hypervisor Framework,在后台运行完整的 Android 环境,再通过 scrcpy 将应用画面呈现到独立的 Mac 窗口中。目前项目仍处于早期预发布阶段,要求 Apple Silicon Mac 和 macOS 15 或更新版本,不包含 Google Play 商店及 Google 服务。

参与讨论