高热度技术教程,拆解从零构建原生 Mac 应用的全流程,对 Claude Code 用户有参考价值
我最近发布了 Context,一款用于调试 MCP 服务器的原生 macOS 应用。目标是打造一个实用的开发者工具,在平台上有宾至如归的体验,由苹果的 SwiftUI 框架驱动。自 2008 年以来,我一直在为 Mac 开发软件,但这次不同:Context 几乎 100% 由 Claude Code 构建。帮助 Claude 构建软件仍然需要技巧和迭代,但在这个项目的 20,000 行代码中,我估计自己手写的代码不到 1,000 行。
这是一篇长文,解释我的历程、如何选择工具、这些工具擅长和不擅长的地方(目前),以及如何利用它们最大化生成代码的质量,尤其是在构建像我这样的原生应用时。
我第一次接触 AI 编码工具是在尝试 GitHub Copilot,内置于 VS Code。这是同类中的第一个工具,我很惊喜:当时它只是自动补全,但效果出人意料地好——与典型编辑器只能自动补全符号名或函数签名不同,它能基于周围的上下文自动补全整个函数实现。这是一个很大的生产力提升,但仍然感觉你在做大部分工作。
随后事态发展加速:Cursor 火爆,它们增加了 Agent Mode,新的竞争者如 Windsurf 加入。所有产品都在倾向于"智能体"开发模式,即 LLM 不是对自动补全进行一次性响应,而是在循环中调用各种工具来完成更复杂的任务:收集代码库上下文、阅读网页和文档、编译程序、运行测试、迭代构建/测试失败,等等。
我当时没有广泛尝试这些新工具,因为没有积极开展副业项目,但在 2025 年 2 月,一个有趣的竞争者突然出现:Claude Code 不像其他产品那样是 VS Code 的分支,而是一个设计用来完全在终端中使用的 IDE。它没有传统的代码编辑能力或功能繁多的压倒性 UI:它把智能体循环放在最前面。只是一个输入提示的文本框,没有太多其他。它不是用 AI 增强你的 IDE,而是替换了你的 IDE。我不完全确信这是理想的用户体验,但相比已有的东西,这个想法足够新鲜,我决定必须尝试一下。
就像许多工作要求很高的工程师一样,我有一个巨大的"墓地",里面是从未发布的副业项目。建造工作原型是可行的,但最后 20% 花费太多时间和精力,以至于我 6 年来都没有发布任何副业项目。
此时,我开始试玩 Claude Code 及其对 MCP(Model Context Protocol,模型上下文协议)服务器的支持。Anthropic 将 MCP 设计为开放标准,允许智能体访问工具和其他上下文以完成特定任务。例如,Sentry MCP 服务器公开工具,允许智能体获取包含堆栈跟踪和其他有用调试上下文的问题,甚至调用 Sentry 自己的漏洞修复智能体。
然而,构建和测试 MCP 服务器的体验很繁琐:MCP 服务器通过标准输入/输出流或 HTTP 与 Server-Sent Events(SSE)与客户端通信,以赋予服务器向客户端流式传输响应的能力。这不像调用 CLI 或使用 curl 向服务发送请求那样简单。有一个名为 MCP Inspector 的第一方工具允许开发者测试服务器功能,但作为长期的 macOS 和 iOS 开发者,我想尝试构建一个原生应用来解决这个问题。我认为这将是推动 AI 智能体边界的绝佳学习体验,希望能从中获得一个有用的产品。
让我直说,Claude Code(配合最新的 Sonnet 4 和 Opus 4 模型)确实擅长写代码。它肯定不是前 1% 的程序员,但我会说 Claude 的输出远好于普通开发者。给定你想实现的功能描述,Claude 可以:
真正了不起的是,它完成所有这些的时间仅为一个人实现整个功能所需时间的一小部分。想象一下,一个对你的项目毫无了解的新员工入职,几分钟后就能发布一个完整的功能。
我决定使用最新的苹果开发者技术来构建我的应用:macOS 15.5 上的 Swift 6.1 和 SwiftUI。我很好奇 Claude 在编写 Swift 方面的表现,因为与 Python 或 JavaScript 等更普遍的语言相比,模型训练数据中的 Swift 代码要少得多。
好消息是,Claude 能够胜任大多数 Swift 语言功能,直到 Swift 5.5 引入 Swift Concurrency。Swift Concurrency 是语言的剧烈变化,在我看来,即使对人类来说也很难正确使用。Claude 在现代框架和遗留等效物之间选择时也会感到困惑。当有更现代的 Swift 替代品可用时,它往往会尝试使用遗留的 Objective-C API,或者使用 AppKit/UIKit 而不是 SwiftUI。
它生成的 SwiftUI 代码相当有效:通常是 UI 的准确(但有些丑陋的)表现,进一步迭代可以将其转变为真正感觉良好设计和可用的东西。
Claude 在生成 UI 代码时不断遇到的一个问题是 Swift 本身的根本性问题:UI 代码的类型表达式往往变得非常复杂,以至于编译器失败并出现令人畏惧的"The compiler is unable to type-check this expression in reasonable time"错误。解决方案是将视图体重构为更小的表达式,幸运的是,Claude 在不破坏实现的情况下非常擅长这样做——当它在输出中看到该编译器错误时,有时甚至会自己做这件事。
你可以通过创建一个 CLAUDE.md 文件并提供关于使用现代 API 的基本说明来让 Claude 避免常见陷阱。以下是我的 Context 项目的 CLAUDE.md 文件中的一个片段:
* Aim to build all functionality using SwiftUI unless there is a feature that is only supported in AppKit.
* Design UI in a way that is idiomatic for the macOS platform and follows Apple Human Interface Guidelines.
* Use SF Symbols for iconography.
* Use the most modern macOS APIs. Since there is no backward compatibility constraint, this app can target the latest macOS version with the newest APIs.
* Use the most modern Swift language features and conventions. Target Swift 6 and use Swift concurrency (async/await, actors) and Swift macros where applicable.
即使这种相对较低努力的规则集也能产生合理的结果,但你可以走得更远:例如,Peter Steinberger 的 agent-rules 仓库包含规则,你可以将其添加到你的智能体,用于通用编码准则和更具体地编写更好的 Swift 代码。
如果你想自己判断代码质量,请查看我项目中的这些示例:
OAuthClient.swift: OAuth 2.1 implementation
JSONOutlineView.swift: SwiftUI view that renders JSON in a tree structure with support for expanding/collapsing nodes
如果 Claude 第一次没有生成设计良好的 UI,你可以直接告诉它"make it more beautiful/elegant/usable"。我发现对于这么少的努力,结果出人意料地好。你也可以更系统地做这件事,首先要求它"come up with suggestions for how to make this UI more beautiful",它将生成一个你可以选择的设计调整列表。
如果你发现了 UI bug 或想调整某个 UI 元素,可以截图并将其拖放(或 ⌘+V 粘贴)到 Claude Code 中。未来可能会有更好的自动化方案,但目前这种方法效果很好,而且通用——无论你用的是哪个前端平台。
随着 AI 走向主流,业界迅速定义了一个新学科:提示工程。提示工程的思想是,你需要精心编写提示词来从模型中提取最高质量的输出。那时可能是这样的,但根据我的经验,我发现在使用更新型的模型时,提示工程是错误的关注点。
如今的模型在处理不完美输入和理解意图方面做得好得多,既因为模型本身更强大,也因为它们融入了思维链(CoT)提示。你可以用模糊的描述、不完整的句子、错误的拼写和语法来提示模型,它仍然能相当好地理解你的要求,并将问题分解成一系列步骤。
使用 Claude Code 或类似工具时,你要不断面对的限制是上下文窗口。Anthropic 最新的两个模型(Sonnet 4 和 Opus 4)都有 200k 的上下文窗口,意味着它们一次可以处理相当于 200k tokens 的文本。每个提示和响应都会消耗更多上下文,而且模型在上下文窗口末尾的表现往往会下降。
Claude 甚至会有帮助地显示一个指示器,显示你剩余的上下文量,之后它会开始"压缩"对话。压缩意味着它会总结当前对话,并用这个总结为新的上下文窗口提供种子,这样你就可以继续提示。压缩并不完美——它可能会遗漏之前对话中的重要细节,或者用之前错误产生的低质量上下文为新上下文提供种子。
使用有限的上下文 tokens 生成最高质量的输出,换句话说,上下文工程,是有效使用编码智能体的主要挑战。
有一个我称为"激活"智能体的过程,与其让智能体直接跳到执行任务,我会让它预先读取额外的上下文,以增加它生成良好输出的可能性。
默认情况下,它会读取用户范围和项目范围的 CLAUDE.md 文件中的内容,但你可以通过要求它读取特定的文档或源代码来拉入额外的任务特定上下文。这是我最近用过的一个提示,用来让它读取一些现有的源代码和来自网络的规范:
Read DXTTransport.swift, DXTManifest.swift, DXTManifestView.swift, DXTConfigurationView.swift, DXTUserConfiguration.swift, AddServerFeature.swift, and AddServerView.swift to learn how adding servers from DXT packages is implemented.
Then read the documentation for the manifest.json format here: https://raw.githubusercontent.com/anthropics/dxt/refs/heads/main/MANIFEST.md
After reading these sources, summarize what you've learned.
Claude 随后会使用搜索和读取工具来查找和读取源文件,并使用获取工具从 GitHub 下载 Markdown 文件。要求它进行总结会强制它思考它从源代码中理解了什么,拥有这个总结在上下文中会改进后续任务的性能。
当你的代码使用第三方依赖或可能在模型知识截断日期之后引入的新 API 时,激活尤为重要。Context7 和 llm.codes 这样的工具存在于解决将文档格式化为模型可消费的纯文本格式的问题。
当要求 Claude 构建功能时,拥有详细的规范对于引导模型至关重要。如果不费力,Claude 无法构建任何非平凡的功能。AI 产品演示通常会强调用一句话提示就能创建"完整应用",但如果你想要的不仅仅是原型,你需要一个真正的规范。
规范不需要写得很好。你甚至可以进行语音口述(我仍然更喜欢打字,但任何方式都可以)。这是我给 Claude 的一个规范示例,用来在我的应用中构建新功能:
这似乎很多,但我能够比实现这个功能快得多的速度打出这个。
Claude 倾向于在背景不足的情况下直接跳到实现,这会产生低质量的结果。激活智能体的另一个策略是要求 Claude 使用其扩展思考模式并先制定计划。扩展思考由这组魔法关键词激活:"think" < "think hard" < "think harder" < "ultrathink"。这些不仅仅是对模型的建议——它们是激活各种级别扩展思考的特定短语。Ultrathink 消耗最多的 tokens,但会产生最好的结果。如果你想迭代计划,在提示中显式包含指令以在用户接受计划之前不继续实现会很有帮助。
总体而言,我强烈推荐阅读 Anthropic 的《Claude Code:智能体编码最佳实践》文章。我在这里讨论的许多技术都在该文章中有所涉及,它应该被认为是充分利用 Claude Code 或任何编码智能体的必读内容。
Claude 在能够独立驱动反馈循环时最有用,允许它进行更改、测试更改、并收集有关失败内容的上下文以尝试另一次迭代。关键循环包括:
构建。Claude 应该知道如何编译你的应用。Claude 知道如何通过 swift build 编译 Swift 包,但对于我的 macOS 应用目标,它经常无法找出正确的 xcodebuild 调用。XcodeBuildMCP 通过为模型提供一套简化的构建和运行应用工具来解决问题。
测试。Claude 应该能够构建和运行你的测试并查看测试输出。同样,Claude 能够对 Swift 包通过 swift test 开箱即用地做这个。我还没有测试它是否可以运行应用/UI 测试,但我怀疑 XcodeBuildMCP 可能也是必需的。
修复 Bug。Claude 已经知道如何通过添加调试日志来调试问题。问题是它无法像用户那样与应用交互来使应用进入发出正确日志的状态。你将不得不手动与应用交互,并将日志从控制台复制/粘贴到 Claude。这样可以,但这意味着除非你预先写单元测试或 UI 测试来封装行为,否则你无法让它完全自主地修复问题。存在针对浏览器应用的自动化解决方案,如 playwright-mcp,但我不知道对本地开发有充分测试的等效方案。
修复 UX 问题。我之前提到你可以将屏幕截图粘贴到 Claude 中,让它迭代 UI。你可能能够使用 Peekaboo 之类的工具来自动获取屏幕截图,但你仍然会遇到需要手动与应用交互以首先使其进入正确状态的问题。
因为 Claude Code 是包装通用模型的 AI 智能体,你在迭代应用本身时仍可以使用它来帮助非编码任务。比如编辑文案或通过向模型请求有关如何改进应用功能的建议来规划功能版本。
我发现有用的一个小东西是在我有办法将真实数据导入应用之前生成模拟数据的能力。在构建 Context 时,我已部分构建了 Swift MCP 客户端库的实现,但我想改变方向做一些 UI 原型设计。通常,生成逼真的模拟数据的过程会非常乏味,我永远不会尝试,但 Claude 在几秒钟内生成了很好的模拟数据。当我调整 UI 时与朋友分享的应用的第一批屏幕截图是由模拟数据支持的,但看起来足够真实,你可以很好地了解当从真实 MCP 服务器呈现数据时应用会是什么样子。
特别是对于 MCP,模拟数据更加重要,因为当时大多数 MCP 服务器没有使用规范中工具之外的大部分功能,但我仍然需要一种方法来验证这些功能的 UI。
在完成这个项目的过程中,我意识到自己始终只用了两个工具:Claude Code 和 GitHub Desktop(用来查看 diff)。大部分时间里,我根本不需要传统编辑器的任何功能:文件树、源代码编辑器、扩展等。我偶尔会手动用 Xcode 做一些编辑,但这很少见,而且我仍然没有用到大部分 Xcode 特定的功能(SwiftUI Previews、View Debugger 等)。鉴于这是 coding agent 永远都会处于的最差状态,我不得不想象,在某个未来,IDE 的样子会与今天完全不同。
Cursor、Windsurf 和 Copilot 都是从 VS Code 开始然后各自演进,但它们本质上都是在一个前 AI 时代设计的编辑器上添加 AI。从根本上说,VS Code 看起来与 20 年前的 JetBrains IDE 没有太大区别。我也看到了像 Warp 这样的项目,试图从现代化终端模拟器转向智能体开发环境,但我认为终端不一定是理想的 UX,尽管我非常喜欢 Claude Code。
我认为未来的 IDE 将专注于帮助开发者为智能体准备上下文,并建立对智能体成功完成任务至关重要的反馈循环。这方面的 UX 会看起来非常不同——我无法准确预测具体样子,但我不认为源代码编辑器会是中心。
整个过程中最令我兴奋的,不是我构建的应用本身,而是我现在能够再次追随自己的编码热情,发布打磨过的副项目。就像我每天多出了 5 小时,而代价仅是每月 200 美元。