当 Build in Public 重新转入地下 -- 肘子的 Swift 周报 #157
当 Build in Public 重新转入地下过去一段时间社区里频繁出现用 AI 快速逆向商业产品或像素级复刻热门应用的案例。只要 Token 充足搭建一个免费的“影子版本”似乎已不再是难事。与此同时许多坚持“公开构建”Build in Public的独立开发者也遭遇了新的挑战原创产品尚在打磨之中一些仿制者仅凭作者在社交媒体上披露的设计截图和思路便借助 AI 抢先一步上线了“克隆品”。有的源于技术探索与好奇有的只是想展示 AI 工具的生产力但也不乏投机取巧的捷径心态。看着这股风气我始终感到一丝不安。复刻与模仿在过去几十年中一直都存在但 AI 大幅压缩了实现所需要的时间与成本。原创者可能花费数月才找到一个值得做的方向模仿者却可以观察最终结果跳过大量试错在极短时间内做出一个外观和功能都颇为相似的版本。商业竞争当然需要鲶鱼AI 对原创者同样有所帮助。如果更低的实现门槛能够促使原创团队加快创新、改善体验或者提供更具性价比的服务自然是消费者乐于看到的结果。更何况复制界面与功能并不等于复制产品本身那些隐藏在细节中的设计判断和对用户需求的理解并不是一次像素级复刻就能获得的。但并不是每个原创者都有足够的时间和资源等待市场认识到这些差异。对于成熟的商业产品几个 AI 仿制品或许不足以构成真正的威胁但对于仍在寻找市场位置的独立开发者几周甚至几个月的时间差就可能非常重要。曾经通过 Build in Public 分享创意、设计与开发过程是独立开发者建立社区、获得反馈的一种方式。更重要的是它让大家愿意在产品还不成熟的时候就拿出自己的想法、尝试甚至失败与其他开发者交流。这种氛围并不是天然存在的而是这些年一点点形成的。但当公开的信息同时成为别人寻找复刻目标、快速跳过探索阶段的捷径分享所需要承担的风险也发生了变化。如果越来越多的开发者发现在产品真正准备好之前保持沉默比公开分享更加安全那么 Build in Public 或许真的会重新变成 Build in Private。这才是最让我不安的地方。我们失去的可能不只是几个尚未问世的原创产品而是独立开发者好不容易形成的、愿意彼此展示不成熟想法并相互启发的交流氛围。本期内容 | 前一期内容 | 全部周报列表原创AI-Friendly Framework当抽象上移验证如何跟上随着 AI Coding 的普及不少人开始倾向一种观点UIKit 这样的命令式框架可能比 SwiftUI 更适合 Coding Agent。理由很直观——UIKit 的逻辑直接明确创建对象、设置属性、加入层级、建立约束每一步都有具体操作的实体而 SwiftUI 把大量实现细节收束在声明式语法之后状态、环境、身份、布局与渲染共同决定了最终结果。一旦呈现效果偏离预期Agent 只能对着黑盒从有限的外部现象去反推内部究竟发生了什么。但问题或许并不在抽象本身而在于抽象之后我们漏掉了另一半工作观察与验证。当抽象隐藏了底层细节我们必须在新的层级上补齐能够固定并验证意图的手段。近期推荐重审「局部隔离」的利弊与权衡 (Revisiting “Split Isolation”)在一个本身没有 actor isolation、且 non-Sendable 的类型中只给部分方法添加MainActor会让同一个实例同时具有不同的隔离要求。Matt Massicotte 将这种模式称为 “split isolation”。包括 Matt 自己在内不少开发者都认为这并非一个好的设计。在本文中Matt 重新审视了自己过去的判断split isolation 虽然仍应尽量避免但在特定场景仍有存在价值。尤其在只有少量独立功能依赖 MainActor或者正在向 Swift 6 迁移、不希望隔离要求迅速扩散到大量既有代码时局部添加MainActor也可能是一种合理甚至必要的权衡。是否采用 split isolation关键在于它是在掩盖类型真正的隔离需求还是在有意识地表达一个局部约束。超越 async/awaitTask 不是业务工作的基本单元 (The Task Is Not the Unit of Work: Ownership, Isolation, and Cancellation Beyond async/await)SwiftUI 的.task会随着视图消失自动取消任务这很方便但也很容易让开发者顺手将各类业务操作的生命周期绑定到 UI 的生命周期。不过页面消失并不意味着用户的意图已经失效。Bruno Valente Pimentel 指出Task 是执行单元却不一定是产品工作的单元Actor 解决 isolation却不会决定谁应该拥有这项工作结构化并发能够传播 cancellation却无法判断这种父子关系是否符合产品语义。相比先考虑“Task 应该放在哪里”Bruno 建议首先明确操作的 intent、owner、resource 和 lifetime然后再选择具体的实现机制。编译器可以帮助我们保证并发安全但无法替我们决定一项工作什么时候才真正“不再重要”。SwiftUI 日历组件的边界与状态设计 (A SwiftUI calendar with custom days and event indicators)实现一个日历 UI真正麻烦的往往不是画出日期网格而是处理背后的状态与边界条件如何将当前显示日期与选择范围分离统一通过Calendar处理日期运算和 day boundary正确应对 first weekday、跨月份、夏令时以及非 Gregorian Calendar点击与拖动如何共同完成连续的范围选择避免每个 cell 重复扫描事件列表避免不必要的 cell 视图重建等挑战。Szymon Lorenz 分享了他为 SwiftUIComponents 开发日历组件的经验这个开源项目通过 Skip Fuse 还提供了 Android 端的兼容性。100,000 个事件下的 SwiftUI 性能排查实战 (SwiftUI Profiling with Instruments: 100,000 Calendar Events)当一个包含大量数据的 SwiftUI 应用出现卡顿时问题究竟来自数据库、数据处理还是视图更新Maciej Szamowski 使用包含 100,000 个事件的测试数据库对 hora Calendar 进行压力测试通过 Instruments 的 SwiftUI、Hangs、Hitches 和 Time Profiler发现卡顿期间大量主线程时间消耗在 SwiftUI graph updates 上从而将优化重点从数据库转向了数据准备和视图更新路径。前后两篇有关日历的文章在改善渲染性能上都采用了类似的优化原则尽量不要让 cell 自己承担那些可以在更高层级完成的重复工作。上层准备数据和交互上下文 → 下层 View 接收尽可能稳定、接近最终展示形式的值 → 与 rendering 无关的信息不要进入 SwiftUI observation/update graph。排查 fastlane snapshot 多语言截图全部变成英语的问题 (Why your fastlane snapshot screenshots are all in English)使用 fastlane snapshot 批量生成 App Store 多语言截图时有时会遇到一个让人头痛的问题语言目录都正确创建了截图内容却全部显示为英语。真正麻烦的是导致这一现象的原因并不唯一排查起来也比较困难。Manuel Lorenzo Parejo 列出了五个排查点setupSnapshot的调用时机snapshot 的启动方式localize_simulatorApp Bundle 中是否包含对应的.lproj残留的 App 状态是否影响语言设置。同时还提供了免费的shotfleet check命令通过识别截图文字并与 App 自身的本地化字符串进行比对批量检查生成的截图是否真的使用了预期语言。工具Redlamp一位摄影爱好者借助 AI 打造的原生 RAW 编辑器Redlamp 是一款采用 Swift 和 Metal 开发的开源 macOS RAW 照片编辑器使用 MPL-2.0 许可证沿用了 Lightroom 用户熟悉的显影面板与操作习惯。作者 Pedro Gomes 既是一名工程师也是一名摄影爱好者一直希望有更多符合自己使用方式的工具选择。在这个大量借助 AI 进行开发的项目中仍能看到鲜明的个人意图与判断Pedro 选择让照片库的数据库成为可重建的索引将重要信息保存在照片旁也会在测量发现蒙版边缘优化损失了细节后收紧算法的适用范围。这些具体取舍让 Redlamp 带有鲜明的作者印记。项目博客记录了不少开发过程中的思考代码库也公开了面向编程代理的协作规则与验证机制。Pedro 正在探索如何让多个代理在持续迭代中参与复杂原生软件的开发以及如何判断它们交付的成果是否可靠。AnnotateIt在 Mac 上完成从标注到 Create ML 训练的闭环对想用 Create ML 训练自定义 Object Detection 模型的开发者来说训练本身往往不是最麻烦的部分。真正耗时的是数据准备Create ML 应用本身不提供标注功能开发者通常需要借助其他工具画框再将结果整理成 Create ML 可以使用的数据格式。AnnotateIt 把这些步骤放进了同一个 Mac 应用。完成图片标注后可以直接通过 Apple Create ML 在本地训练 Object Detector训练得到的模型又可以用于新的图片和视频辅助生成标注并由人工继续修正。对于 Swift 开发者而言它不仅降低了尝试本地 Object Detection 的门槛也把「标注 → 训练 → 预测 → 修正」连接成了一套完整的工作流。在官方的停车场示例中292 张图片的模型训练不到一分钟即可完成。整个过程不需要配置 Python 环境或云端 GPU数据也可以始终留在本机。InnoDI让依赖图成为可验证的构建产物Ethan Parker 开发的 InnoDI 是一套基于 Swift Macro 的依赖注入框架。它将对象图中原本需要手工编写的初始化与参数传递逻辑转化为显式的依赖声明并生成容器初始化器和类型安全的测试替换接口。Macro 负责局部诊断SPM Build Plugin 验证跨文件、跨模块的依赖图配套 CLI 则可以对这张图进行检查和分析。InnoSquad 的 InnoFlow、InnoRouter 和 InnoNetwork 也延续了类似思路通过明确边界和可重复验证来减少隐式行为把能够声明的约束提前声明把运行时行为交给统一的执行策略管理。在 AI Coding 时代框架作者需要重新思考框架的消费者。“让开发者更容易编写代码”或许已不再是唯一的首要目标。当学习成本不再完全由人类开发者承担“让代码的意图和行为更容易被验证”的重要性正在迅速提高。SimAgentation在浏览器中为 iOS 模拟器标注并交办 AI使用 AI 调整界面时如何准确表达“这里需要修改”往往比描述修改本身更费力。citron 开发的 SimAgentation 让开发者在浏览器中操作 iOS 模拟器IOSurface - VideoToolbox - WebCodecs选中界面元素或框选区域直接写下修改意见。工具会将反馈连同元素信息、源码搜索线索、整屏截图和局部截图通过 MCP 交给编码助手并在浏览器中显示处理状态。可选的 SimAgentationPlus 则支持通过.simTag()为视图补充名称、文件和行号。这与我在 FlowCanvas 中采用的元素锚点思路有相通之处将画面与结构化信息结合让 AI 更准确地理解反馈对象。往期内容Swift Server又多了一个 Google - #156iPhone Duo 留给开发者的一个月 - #155一场关于 SwiftUI 动画的“原生”之争 - #154iPhone Duo 带来的机遇与挑战 - #153 支持与反馈如果本期周报对你有帮助请点赞- 让更多开发者看到评论- 分享你的看法或问题转发- 帮助同行共同成长拓展 Swift 视野 邮件订阅| weekly.fatbobman.com 获取独家技术洞察 开发者社区| Discord 实时交流开发经验 原创教程| fatbobman.com 学习 Swift/SwiftUI 最佳实践