Fatbobman's Swift Weekly #0101
From Open Platform to Controlled Ecosystem: Google Announces Android Developer Verification Policy
From Open Platform to Controlled Ecosystem: Google Announces Android Developer Verification Policy
Google has announced that starting September 2026, it will extend Play Store developer verification requirements to all Android app installation methods, fundamentally altering the platform's open distribution model. This policy requires all developers distributing apps outside Google Play to register with Google, provide government-issued identification, and pay fees. The policy will first be implemented in Brazil, Indonesia, Singapore, and Thailand, before expanding globally in 2027. This represents Android's most significant departure from its founding principles of an open ecosystem.
While Google emphasizes that the primary reason for implementing this policy is security—malware from internet-sideloaded sources is over 50 times more prevalent than apps on Google Play, and restricting arbitrary app sideloading can effectively combat fraud—Android ecosystem developers have expressed strong resistance to this change, unlike their Apple ecosystem counterparts who have long been accustomed to similar policies. Many developers state they are unwilling to provide identity documents to Google for legitimate privacy, political, or security reasons. For open-source app stores like F-Droid, this policy poses an existential crisis—the community estimates that approximately 85% of apps may be unable to adapt to the new requirements due to technical issues such as signing key conflicts.
From adjusting the AOSP public branch release cycle (Android core development will be entirely concentrated in internal branches, no longer partially developed and merged in real-time on AOSP's public branch as before), raising AOSP licensing thresholds, strengthening Play Store app review processes, to now implementing developer verification for sideloaded apps, Google's tightening of Android policies has gone further and further. This reflects both Google's intent to transform Android into a "walled garden" similar to iOS, and its urgent need to deepen Android's commercial value in response to unfavorable factors such as Chrome's potential breakup and AI's sustained impact on search business.
Perhaps the final differences between Android and iOS are disappearing—this is both a crisis and potentially an opportunity. Android's widespread adoption benefited from its open nature, and under current circumstances, this may provide a favorable moment for some new platform or ecosystem to emerge. Of course, for commercial entities, open source versus closed source, openness versus closure, are merely means to achieve greater economic value, and they will flexibly switch between these strategies to maximize their interests. Therefore, even if new disruptors emerge, similar cycles will likely repeat in the future. Society develops through such spirals of change.
Previous Issue|Newsletter Archive
If you appreciate my work and want to promote your product to the Swift and iOS developer community, sponsoring my blog & newsletter could be an excellent opportunity for you.
This Week's Sponsor
Need to debug HTTPS on your iPhone?
Try Proxyman! The best-in-class macOS that helps you capture/debug HTTP(s) with a few clicks. Support iOS devices and Simulator.
Original
Using MainActor.assumeIsolated to Solve Legacy API Compatibility Issues with Swift 6
While Swift has offered strict concurrency checking for some time, many of Apple’s official APIs have yet to be fully adapted, and this situation may persist for quite a while. As Swift 6 gradually gains adoption, this problem becomes increasingly prominent: developers want to benefit from the concurrency safety guarantees provided by the Swift compiler, while struggling with how to make their code meet compilation requirements. This article will demonstrate the clever use of MainActor.assumeIsolated
in specific scenarios through an implementation case with NSTextAttachmentViewProvider
.
Recent Recommendations
When should you use an actor?
In Swift’s concurrency model, actor
is a new kind of reference type designed to safely protect mutable state and avoid data races. Naturally, many developers are drawn to it once they understand its capabilities. However, as Matt Massicotte points out, actors come with trade-offs—such as enforced asynchronous access and Sendable
requirements—and they’re not a one-size-fits-all replacement for GCD. In this article, the author dives deep into the type system and concurrency model to clarify where actors are appropriate, common misuses, and how to make better architectural decisions.
For more community insights, check out this Reddit thread.
SwiftUI is Stifling your App’s Maintainability and Testability
Unlike UIKit, SwiftUI doesn’t prescribe a clear architectural pattern. Its declarative style and reactive data flow challenge many of the traditional assumptions around organizing state and logic. In this article, Matteo Manferdini responds to popular opinions that claim MVVM is outdated or incompatible with SwiftUI. He systematically defends the use of view models, grounded in software design principles like encapsulation, SRP, and SOLID, and highlights the long-term testability and maintainability issues that arise from avoiding them.
Personally, I lean toward using view models when appropriate—but never for the sake of abstraction alone. In SwiftUI, overengineering can lead to unnecessary boilerplate and state complexity. Architecture should serve the app's needs, not the other way around.
How Large is That File?
If your macOS app displays file or folder sizes, you've likely noticed discrepancies with what Finder shows. That’s not necessarily a bug in your code—Finder and system APIs often report different values and omit crucial parts of the file. In this article, Howard Oakley walks through the evolution of macOS file systems—from Classic Mac OS to APFS—to explain why the question “How large is that file?” remains surprisingly complicated.
Howard argues that macOS still relies on legacy logic from 25 years ago when calculating file size. Especially with modern features like extended attributes (xattrs
) and resource forks, what you see in Finder can be incomplete or even misleading—posing challenges for developers dealing with backup systems, permissions, or disk usage audits.
Automating Swift Binary Releases Using GitHub Actions
When hosting code on GitHub, developers often want to provide ready-to-use binaries—sometimes instead of, or in addition to, source code. This process can be fully automated using GitHub Actions. In this guide, Tiago Henriques demonstrates how he uses GitHub CI/CD to automatically build and publish a macOS binary for his Swift command-line tool, SwiftDummyGen. It's a practical reference for developers looking to streamline their release process.
Swift Default Value in String Interpolations
Optional values are a powerful feature in Swift, but they’ve always been awkward to deal with in string interpolation. Until now, developers had to use ??
(with type-matching constraints) or String(describing:)
(which outputs "nil"
). Swift 6.2 introduces a cleaner solution: a default:
parameter in string interpolation, allowing developers to define fallback text for optional values.
In this article, Keith Harrison explains how this new feature works, along with a caveat—LocalizedStringKey
doesn’t support it yet.
macOS Accessibility
Compared to iOS, macOS’s multi-window architecture introduces additional challenges for developers working on accessibility features—especially for third-party input methods. In this article, zonble shares his journey in implementing VoiceOver support for a macOS input method: from resolving focus-related issues that caused the candidate window to disappear, to using the NSAccessibility
notification system to manually control VoiceOver behavior, and finally digging into system SQLite databases to retrieve phonetic annotations, radicals, and example words for Chinese characters.
The article blends hands-on implementation with low-level exploration, offering insights into how macOS accessibility truly works—while also shedding light on some of the “black box” behavior in how Apple handles spoken feedback for input methods.
By reverse-engineering several system SQLite databases, the author uncovers how macOS’s built-in input method provides phonetic descriptions and structural explanations for characters. Beyond being a practical trick, this discovery offers valuable inspiration for more advanced accessibility implementations.
Tools
OpenAttributeGraph Documentation
At the heart of SwiftUI lies a lesser-known but critical component: AttributeGraph. It powers dependency tracking and enables efficient, incremental UI updates. But because it's private, developers have had limited access to its inner workings—until now.
The OpenAttributeGraph project, part of the OpenSwiftUI initiative, recently published comprehensive documentation explaining its open-source implementation. This helps developers better understand how SwiftUI manages state dependencies and serves as a foundation for both contributing to OpenSwiftUI and optimizing their own apps.
Subprocess
Swift officially introduces for the aging Process
/NSTask
APIs: Subprocess 0.1, a new library designed for cross-platform process management. Built with Swift’s concurrency model in mind, Subprocess supports native async/await
, type-safe input/output, and consistent behavior across macOS, Linux, and Windows.
Whether you're building CLI tools, server-side apps, or need to invoke system commands in a macOS app, Subprocess offers a clean and ergonomic interface:
let result = try await run(.name("ls"), output: .string(limit: 4096))
print(result.standardOutput ?? "")
Currently requires Swift 6.1+ and does not yet support iOS. As part of the Swift Foundation project, Subprocess reflects a broader effort to modernize Swift’s core libraries.
从开放平台到受控生态:谷歌宣布 Android 开发者验证政策
谷歌宣布从 2026 年 9 月起,将 Play 商店的开发者验证要求扩展到所有 Android 应用安装方式,这从根本上改变了该平台的开放分发模式。这项政策要求所有在 Google Play 之外分发应用的开发者必须向谷歌注册、提供政府身份证明并支付费用。该政策将首先在巴西、印度尼西亚、新加坡和泰国实施,2027 年扩展至全球。这代表着 Android 自诞生以来对其开放生态系统原则的最重大背离。
尽管谷歌强调实施该政策的主要原因是安全考量——来自互联网侧载源的恶意软件比 Google Play 上的应用多 50 倍以上,限制任意来源的应用侧载可以有效改善欺诈问题。但与苹果生态的开发者早已习惯类似政策不同,Android 生态的开发者对这一改变表达了强烈的抵触情绪。许多开发者表示,出于合理的隐私、政治或安全原因,不愿向谷歌提供身份文件。对于 F-Droid 等开源应用商店而言,这一政策更是生存危机——社区估计约 85% 的应用可能因签名密钥冲突等技术问题而无法适应新要求。
从调整 AOSP 公共分支的发布节奏(Android 核心开发将全部集中到内部分支进行,不再像过去那样部分内容在 AOSP 的公共分支实时开发和合并)、提高 AOSP 的授权门槛、强化 Play 商店的应用审核,到如今针对侧载应用的开发者验证,谷歌收紧 Android 政策的步伐已越走越远。这一方面体现了谷歌意图将 Android 平台打造成类似 iOS 的"围墙花园"模式,另一方面也反映出谷歌急需深化 Android 平台的商业价值,以应对 Chrome 可能被拆分、AI 对搜索业务产生持续冲击等不利因素。
或许,Android 与 iOS 之间最后的差异正在消失,这既是危机,但未尝不是转机。Android 的普及得益于其开放特性,在当下的情况下,或许又给了某个新平台、新生态提供了不错的发展时机。当然,对于商业机构而言,开源与闭源、开放与封闭往往都是实现更大经济价值的手段,它们会在这些策略间灵活切换以保证自身利益最大化。因此,即便之后出现了新的搅局者,未来还是会重现类似的循环。社会就是在这样的螺旋中不断发展的。
如果您发现这份周报或我的博客对您有所帮助,可以考虑通过 爱发电,Buy Me a Coffee 支持我的创作。
本期赞助
需要在 iPhone 上调试 HTTPS?
试试 Proxyman!这是一款顶级的 macOS 应用,只需点击几下,即可轻松捕获和调试 HTTP(s) 流量。支持 iOS设备和模拟器。
原创
用 MainActor.assumeIsolated 解决旧 API 与 Swift 6 适配问题
尽管 Swift 提供严格并发检查已有一段时间,但许多苹果官方 API 仍未对此进行充分适配,这种情况可能还会持续相当长的时间。随着 Swift 6 的逐步普及,这个问题变得愈发突出:开发者一方面希望享受 Swift 编译器带来的并发安全保障,另一方面又对如何让代码满足编译要求感到困惑。本文将通过一个 NSTextAttachmentViewProvider
的实现案例,介绍 MainActor.assumeIsolated
在特定场景下的妙用。
近期推荐
何时使用 Actor?(When should you use an actor?)
在 Swift 的并发模型中,actor 作为一种新的引用类型,为“可变状态”的保护提供了安全保障,能有效避免数据竞争(data race)。因此,许多开发者在了解这一特性后,往往会对 actor 倾心不已。但正如 Matt Massicotte 在本文中所指出的,actor 并非没有代价。它带来了异步访问、Sendable 要求等设计限制,也并不能完全取代 GCD。在本文中,作者结合 Swift 的类型系统与并发模型,深入探讨了 actor 的适用场景、常见误区,并总结出一套清晰的判断标准,帮助我们在设计时作出更合理的选择。
如果你希望更深入地探讨这篇文章的内容,不妨参考 这个 Reddit 帖子,社区中有不少精彩的观点。
为什么 SwiftUI 中需要 ViewModel (SwiftUI is Stifling your App’s Maintainability and Testability)
与 UIKit 不同,苹果并没有为 SwiftUI 提供明确的架构范式,而 SwiftUI 的声明式语法和响应式数据流也打破了许多传统的设计假设。因此,在实际开发中,关于状态管理、逻辑组织以及是否应该使用 ViewModel,始终存在不少争议。在这篇文章中,Matteo Manferdini 针对“MVVM 过时论”和“Apple 不推荐 MVVM”的观点,提出了系统性的反驳。他不仅从软件工程原则(如封装、SRP、SOLID)出发分析了使用 ViewModel 的优势,还通过对实际代码的分析,指出忽视 ViewModel 带来的可测试性与可维护性问题。
我个人倾向于在实践中使用 ViewModel,但也不会为了“架构”而“架构”。在 SwiftUI 中,过度抽象反而可能带来样板代码和额外的状态管理成本。架构的选择,始终应该服务于项目的复杂度与可维护性目标。
macOS 文件大小的迷思 (How Large is That File?)
如果你的 macOS 应用中包含文件或文件夹大小显示功能,你可能已经发现:显示结果与 Finder 并不总是一致。这并不一定是你代码的问题,而是因为 Finder 和系统 API 所提供的信息本身就存在偏差,而且它们默认会忽略许多重要的文件内容部分。在本文中,Howard Oakley 从一个看似简单的问题出发——“一个文件到底有多大?”——带领读者回顾了 macOS 文件系统从 Classic Mac OS 到 APFS 的演变过程,并对这一看似简单却充满历史遗留与实现细节的问题进行了系统性的梳理。
Howard 指出,macOS 在计算文件大小时的逻辑仍然延续了 25 年前的架构思维,尤其是在处理 xattrs(扩展属性) 与 resource fork(资源分叉) 等结构时,展示结果缺乏一致性与透明度。在 APFS 和现代沙盒系统普及的今天,macOS 对“文件大小”的展示方式仍显得过于简化甚至误导性强,这对备份、权限审计和开发工作都可能带来困扰。
使用 GitHub Actions 自动发布二进制文件 (Automating Swift Binary Releases Using GitHub Actions)
在使用 GitHub 管理代码时,开发者有时不仅需要托管源代码,也希望同步提供可运行的二进制版本,甚至只发布二进制文件。这个流程其实可以完全依赖 GitHub 的 CI/CD 系统自动化完成。Tiago Henriques 以他的 SwiftDummyGen 项目为例,展示了如何通过 GitHub Actions 自动构建并发布 Swift 命令行工具的 macOS 二进制文件,为希望提升发布效率的 Swift 开发者提供了一个简洁实用的参考范式。
Swift 字符串插值的默认值 (Swift Default Value in String Interpolations)
可选值是 Swift 的一个强大特性,但在字符串插值时却带来了一些麻烦。以往当处理可选值时,要么使用 ??
提供默认值(但必须匹配原类型),要么使用 String(describing:)
(会显示 nil
)。Swift 6.2 引入的新特性优雅地解决了这个问题:通过 default
参数,可以为任意类型的可选值提供统一的默认字符串。Keith Harrison 在本文中详细介绍了该功能的用法,并指出了当前的一个限制:LocalizedStringKey
尚不支持此特性。
macOS 的 Accessibility
与 iOS 相比,macOS 的多窗口机制为开发者在实现无障碍功能(Accessibility)时带来了额外挑战,尤其是对于第三方输入法。在本文中,zonble 分享了他为 macOS 输入法实现 VoiceOver 支持的完整过程:从解决焦点抢占导致选字窗无法显示的问题,到使用 NSAccessibility
通知机制手动控制 VoiceOver 的播报行为,最后深入系统中的 SQLite 数据库,获取字符的注音、部首和范例词信息。整篇文章融合了实践技巧与底层探索,不仅帮助开发者理解 macOS Accessibility 的实际工作方式,也揭示了苹果系统在处理输入法语音反馈方面的一些“黑盒子”设计。
作者通过逆向分析多个系统 SQLite 数据库,揭示了 macOS 内建注音输入法如何提供字符的语音描述与结构讲解。这不仅是实用技巧,也为 Accessibility 的深入实现提供了启发。
工具
OpenAttributeGraph 文档
SwiftUI 的核心运行机制背后,有一个至关重要但鲜为人知的组件:AttributeGraph。它负责追踪视图状态与数据依赖,是高效增量更新 UI 的关键引擎。然而,由于是私有实现,一直以来相关资料相当匮乏,深入理解它的机制对开发者而言极具挑战性。最近,OpenAttributeGraph 项目(隶属于 OpenSwiftUI 团队)发布了详尽的文档,对其 AttributeGraph 的开源实现进行了系统讲解。这让开发者不仅能更透彻地了解 SwiftUI 的状态依赖管理机制,也为希望参与 OpenSwiftUI 或优化 SwiftUI 状态更新机制的开发者提供了坚实基础。
Subprocess
Swift 官方发布了 Subprocess 0.1,这是一个全新的进程管理库,旨在替代老旧的 Process
(NSTask)API。Subprocess 完全拥抱了 Swift 的现代特性:原生 async/await 支持、跨平台一致性(macOS、Linux、Windows)、类型安全以及灵活的 I/O 处理。无论是开发 CLI 工具、服务端应用,还是需要在 macOS App 中调用系统命令,Subprocess 都提供了更现代的解决方案。
// 简洁的 API 设计
let result = try await run(.name("ls"), output: .string(limit: 4096))
print(result.standardOutput ?? "")
目前 0.1 版本需要 Swift 6.1+,暂不支持 iOS 等移动平台。作为 Swift Foundation 项目的一部分,它代表了 Swift 生态系统基础设施现代化的重要一步。