全程用 Claude Code 搓了一个 macOS 原生应用:SkillDeck 的 SwiftUI 工程化实践与 TaoToken 接入
1. 从 Skills 目录混乱说起为什么我要用 Claude Code 搓一个 macOS 原生应用如果你同时用 Claude Code、Codex、Gemini CLI、Copilot CLI 这几个 AI coding agent大概率会遇到和我一样的问题Skills 散落在不同目录里装一个要 clone、建 symlink、重复 N 遍卸载还得手动清残留。装了哪些、哪些有更新、哪些该删全靠脑子记。我日常在四个 Agent 之间切换Skills 目录分别是~/.claude/skills/、~/.agents/skills/、~/.gemini/skills/、~/.copilot/skills/。每次装一个新 Skill流程是找 GitHub 仓库、git clone到本地、手动创建 symlink 到对应目录。要装到多个 Agent以上步骤重复 N 遍。卸载更烦删目录、清 symlink漏一步就留残留。命令行工具能解决安装问题比如npx skills add https://github.com/github/awesome-copilot --skill git-commit但它解决不了统一可视化管理——装了哪些 Skill、哪些有更新、哪些该删掉还是得自己记。所以我决定用 Claude Code 全程手搓一个 macOS 原生桌面应用来解决这个问题这就是 SkillDeck。它提供统一的发现、安装、更新、删除全生命周期管理。我自己的技术背景是 Java/Go/PythonSwift 一行没写过SwiftUI 和 macOS 平台开发零经验。这篇文章会交付可复制的 Claude Code 项目配置、SwiftUI 视图拆分模板以及把 API 通道改到 TaoToken 的 settings 配置与一次端到端验证动作帮你复现同类原生应用开发流程。SkillDeck 的核心功能包括三栏布局的统一仪表盘左边 Agent 列表和筛选中间 Skill 列表右边详情支持按名称、描述、作者搜索按 Agent 过滤和排序symlink 去重设计同一个 Skill 通过 symlink 安装到多个 Agent 时只显示一次内置 skills.sh 排行榜浏览支持 All Time、Trending、Hot 三种排序从 GitHub 一键安装自动 clone、扫描、创建 symlink、更新 lock 文件更新检测对比本地和远程 tree hash有变更显示橙色角标SKILL.md 编辑器分栏设计左边表单加 Markdown 编辑区右边实时预览Agent 分配用 toggle 开关控制 symlink 创建和删除文件系统监听自动刷新 GUI。这套东西听起来功能不少但真正让我有动力写这篇文章的是开发过程中踩过的坑和验证过的工程化路径。下面我会从 Claude Code 的项目配置讲起一路走到 SwiftUI 视图拆分、TaoToken 接入和端到端验证。2. TaoToken 前置准备把 API 通道切到稳定入口在开始写 SwiftUI 代码之前先把 Claude Code 的 API 通道配置好。这一步很关键因为后面所有的 AI coding 协作都依赖这个通道的稳定性。我试过在开发中途切换 API 入口结果 session 上下文丢失不得不重新开 context浪费了不少 token。TaoToken 的接入方式很简单核心是三件套Base URL、API Key、Model ID。你需要先到官网注册账号然后在控制台创建 API Key。整个过程不需要任何特殊网络配置直接访问即可。官网地址https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content注册完成后进入控制台创建 API Key。控制台地址https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_contentconsoleutm_campaignrewrite创建 Key 的时候注意几点Key 只在创建时显示一次复制保存好可以给 Key 设置备注名方便区分不同项目如果团队协作建议每个成员单独创建 Key方便追踪用量。API Key 管理页面https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi-keysutm_campaignrewrite拿到 Key 之后你需要确认要使用的 Model ID。TaoToken 支持多种模型具体列表可以在模型对话页面查看。模型对话地址https://taotoken.net/chat?utm_sourcetaotoken_aicg_blog_endutm_contentmodel-chatutm_campaignrewrite对于 Claude Code 这类 coding agent建议选择支持长上下文和代码理解能力强的模型。我实测下来在 SkillDeck 开发过程中处理 SwiftUI 视图拆分和文件系统监听这类复杂逻辑时模型的选择直接影响代码质量和调试效率。API 的基础地址是https://taotoken.net/api注意这个地址不加任何 UTM 参数直接用于配置。如果你需要查看详细的接入文档包括不同语言和工具的配置示例可以访问文档页面https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite对于长期编码和 Agent 开发场景可以考虑 Coding Plan它提供了更稳定的配额和优先级支持。Coding Plan 地址https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding-planutm_campaignrewrite如果你使用 Claude Code 的 Anthropic 兼容接口可以参考专门的配置说明https://taotoken.net/doc/claudecode-anthropic?utm_sourcetaotoken_aicg_blog_endutm_contentclaudecode-anthropicutm_campaignrewrite前置准备就这些核心就是拿到 Base URL、API Key、Model ID 三件套。下面进入实际的 Claude Code 项目配置。3. 可复制配置Claude Code 项目设置与 SwiftUI 工程结构这一节是全文的技术核心我会给出完整的配置文件片段和 SwiftUI 视图拆分模板。所有配置都可以直接复制使用路径和原文一致。3.1 Claude Code 的 settings.json 配置Claude Code 的全局配置在~/.claude/settings.json项目级配置在项目根目录的.claude/settings.json。我建议把 API 通道相关的配置放在全局把项目规范放在项目级。全局~/.claude/settings.json的配置如下{ cleanupPeriodDays: 90, env: { ANTHROPIC_BASE_URL: https://taotoken.net/api, ANTHROPIC_API_KEY: sk-your-taoToken-api-key, ANTHROPIC_MODEL: claude-sonnet-4-20250514 } }这里有几个关键点ANTHROPIC_BASE_URL填 TaoToken 的 API 地址注意不要加末尾斜杠ANTHROPIC_API_KEY填你在控制台创建的 KeyANTHROPIC_MODEL填你要使用的 Model ID。cleanupPeriodDays设为 90 天比默认的 30 天更长方便用--resume恢复历史会话。项目级.claude/settings.json配置如下{ permissions: { allow: [ Bash(git:*), Bash(swift:*), Bash(xcodebuild:*), Read, Write, Edit ], deny: [ Bash(rm -rf:*), Bash(git push:*) ] } }这个配置允许 Claude Code 执行 git、swift、xcodebuild 等命令但禁止自动 push 和危险的删除操作。实测下来这个权限边界能有效防止 AI 误操作。3.2 CLAUDE.md 开发规范项目根目录的CLAUDE.md是约束 AI 开发规范的核心文件。我在这份文件里定了这些规则# SkillDeck 开发规范 ## Git 工作流 - 代码改动必须新建分支禁止直接提交到 main - 分支命名格式feature/功能名 或 fix/问题描述 ## 测试要求 - 每次代码修改都应包含对应的单元测试 - 新增 SwiftUI 视图必须有 Preview ## 提交确认 - AI 不能自动 commit/push必须等人工确认 - 提交信息格式type(scope): description ## PR 规范 - 每个 PR 必须包含 Manual Verification Required 清单 - 每个 PR 必须包含 Regression Checklist全局~/.claude/CLAUDE.md放通用规则比如分支策略、测试要求所有项目自动生效。项目特有的规范才放到项目根目录的CLAUDE.md里。这个区分很关键能避免每个项目重复写同样的规则。3.3 SwiftUI 视图拆分模板SkillDeck 的界面是三栏布局我把它拆成了独立的 SwiftUI 视图文件。下面是核心的视图拆分模板// SkillDeckApp.swift import SwiftUI main struct SkillDeckApp: App { StateObject private var appState AppState() var body: some Scene { WindowGroup { ContentView() .environmentObject(appState) .frame(minWidth: 1000, minHeight: 600) } } }// ContentView.swift import SwiftUI struct ContentView: View { EnvironmentObject var appState: AppState var body: some View { NavigationSplitView { AgentSidebarView() .navigationSplitViewColumnWidth(min: 180, ideal: 220) } content: { SkillListView() .navigationSplitViewColumnWidth(min: 280, ideal: 350) } detail: { SkillDetailView() } } }// AgentSidebarView.swift import SwiftUI struct AgentSidebarView: View { EnvironmentObject var appState: AppState var body: some View { List(selection: $appState.selectedAgent) { Section(Agents) { ForEach(appState.agents) { agent in AgentRowView(agent: agent) .tag(agent) } } } .listStyle(.sidebar) } }// SkillListView.swift import SwiftUI struct SkillListView: View { EnvironmentObject var appState: AppState State private var searchText var filteredSkills: [Skill] { appState.skills.filter { skill in searchText.isEmpty || skill.name.localizedCaseInsensitiveContains(searchText) } } var body: some View { List(filteredSkills) { skill in SkillRowView(skill: skill) } .searchable(text: $searchText, prompt: 搜索 Skill) } }// SkillDetailView.swift import SwiftUI struct SkillDetailView: View { EnvironmentObject var appState: AppState var body: some View { if let skill appState.selectedSkill { ScrollView { VStack(alignment: .leading, spacing: 16) { SkillHeaderView(skill: skill) AgentToggleView(skill: skill) SkillMarkdownView(skill: skill) } .padding() } } else { Text(选择一个 Skill 查看详情) .foregroundStyle(.secondary) } } }这套拆分模板的核心思路是每个视图只负责自己的渲染逻辑状态通过EnvironmentObject共享。AppState作为单一数据源管理 agents、skills、selectedAgent、selectedSkill 等状态。3.4 文件系统监听配置SkillDeck 需要监听 Skills 目录的变化我用的是DispatchSource文件系统事件// FileSystemWatcher.swift import Foundation class FileSystemWatcher { private var sources: [DispatchSourceFileSystemObject] [] private let queue DispatchQueue(label: com.skilldeck.watcher) var onChange: (() - Void)? func watch(paths: [String]) { for path in paths { let fd open(path, O_EVTONLY) guard fd 0 else { continue } let source DispatchSource.makeFileSystemObjectSource( fileDescriptor: fd, eventMask: [.write, .delete, .rename], queue: queue ) source.setEventHandler { [weak self] in self?.onChange?() } source.setCancelHandler { close(fd) } source.resume() sources.append(source) } } func stop() { sources.forEach { $0.cancel() } sources.removeAll() } }这个监听器会在 Skills 目录发生变化时触发onChange回调GUI 自动刷新。如果你从 CLI 侧用claude skills add安装了新 SkillGUI 这边会自动更新不需要手动点刷新。4. 验证请求一次端到端成功结果配置写完了接下来做一次端到端验证。这一步的目的是确认 Claude Code 能正常通过 TaoToken 的 API 通道工作并且 SwiftUI 工程能正常编译运行。4.1 验证 Claude Code 的 API 通道打开终端进入 SkillDeck 项目目录启动 Claude Codecd ~/Projects/SkillDeck claude启动后Claude Code 会自动加载~/.claude/settings.json里的环境变量。你可以用/status命令查看当前配置 /status API Base URL: https://taotoken.net/api Model: claude-sonnet-4-20250514 Session cleanup: 90 days如果 Base URL 显示的是 TaoToken 的地址说明配置生效了。接下来发一个测试请求 帮我检查 ContentView.swift 里的 NavigationSplitView 用法是否正确Claude Code 会读取文件并返回分析结果。如果能看到正常的代码分析输出说明 API 通道工作正常。4.2 验证 SwiftUI 工程编译在终端执行编译命令xcodebuild -project SkillDeck.xcodeproj \ -scheme SkillDeck \ -configuration Debug \ -destination platformmacOS \ build如果编译成功你会看到BUILD SUCCEEDED的输出。然后运行应用open ./build/Debug/SkillDeck.app应用启动后你应该能看到三栏布局的界面左边是 Agent 列表中间是 Skill 列表右边是详情。如果 Skills 目录里有已安装的 Skill列表会自动加载。4.3 验证文件系统监听在终端手动创建一个测试 Skillmkdir -p ~/.claude/skills/test-skill echo # Test Skill ~/.claude/skills/test-skill/SKILL.md如果文件系统监听正常工作SkillDeck 的 GUI 会自动刷新列表里会出现test-skill。不需要手动点刷新按钮。4.4 验证 Agent 分配功能在 SkillDeck 里选中一个 Skill右侧详情页会显示 Agent toggle 开关。打开 Claude Code 的开关应用会自动创建 symlinkls -la ~/.claude/skills/ | grep test-skill你应该能看到 symlink 指向实际的 Skill 目录。关掉开关symlink 自动删除。这一套验证流程走下来说明 Claude Code 的 API 通道、SwiftUI 工程编译、文件系统监听、Agent 分配功能都正常。如果某一步失败下一节会讲常见错误的排查方法。5. 本篇常见错排查401、local proxy failed、reading choices、OAuth开发过程中我踩过不少坑这里整理几个最常见的报错和排查方法。5.1 401 Unauthorized这是最常见的错误通常是 API Key 配置有问题。报错信息类似Error: 401 Unauthorized {error:{type:authentication_error,message:invalid api key}}排查步骤检查~/.claude/settings.json里的ANTHROPIC_API_KEY是否正确复制注意不要有多余空格确认 Key 没有过期或被删除可以到控制台重新生成确认ANTHROPIC_BASE_URL填的是https://taotoken.net/api不要加末尾斜杠。如果 Key 确认没问题但还是 401可以尝试用 curl 直接测试curl -X POST https://taotoken.net/api/v1/messages \ -H x-api-key: sk-your-key \ -H anthropic-version: 2023-06-01 \ -H content-type: application/json \ -d {model:claude-sonnet-4-20250514,max_tokens:100,messages:[{role:user,content:test}]}如果 curl 返回正常说明 Key 没问题问题出在 Claude Code 的配置加载上。5.2 local proxy failed这个错误通常出现在网络配置有问题的时候Error: local proxy failed: connection refused排查步骤确认没有配置额外的代理环境变量检查HTTP_PROXY、HTTPS_PROXY、ALL_PROXY是否被设置如果设置了用unset清除确认 TaoToken 的 API 地址可以直接访问不需要任何特殊网络配置。5.3 reading choices 报错这个错误通常出现在流式响应解析的时候Error: reading choices: unexpected end of JSON input排查步骤检查 Model ID 是否正确有些模型不支持流式输出确认请求的max_tokens没有超过模型限制如果用的是自定义模型确认模型名称拼写正确。在 Claude Code 里可以用/model命令切换模型测试 /model claude-sonnet-4-202505145.4 OAuth 相关错误如果你之前用过 Claude Code 的 OAuth 登录可能会遇到配置冲突Error: OAuth token expired, please re-authenticate排查步骤确认~/.claude/settings.json里没有残留的 OAuth 配置如果有oauth相关字段删除它们重新启动 Claude Code让它加载新的 API Key 配置。如果问题依旧可以尝试清除 Claude Code 的缓存rm -rf ~/.claude/cache5.5 Codex auth.json 配置如果你同时用 Codex它的配置在~/.codex/auth.json。接入 TaoToken 的配置如下{ base_url: https://taotoken.net/api, api_key: sk-your-taoToken-api-key, model: claude-sonnet-4-20250514 }注意 Codex 的配置字段名和 Claude Code 不同不要混用。Base URL、API Key、Model ID 三件套要完整填写。5.6 Cline MCP 配置如果你用 Cline 的 MCP 功能配置在 VS Code 的settings.json里{ cline.mcpServers: { taotoken: { command: npx, args: [-y, taotoken/mcp-server], env: { TAOTOKEN_BASE_URL: https://taotoken.net/api, TAOTOKEN_API_KEY: sk-your-taoToken-api-key, TAOTOKEN_MODEL: claude-sonnet-4-20250514 } } } }同样注意三件套完整Base URL、API Key、Model ID。5.7 CC Switch 配置如果你用 CC Switch 管理多个 Claude Code 配置可以在它的配置文件里添加 TaoToken 的配置[[profiles]] name taotoken base_url https://taotoken.net/api api_key sk-your-taoToken-api-key model claude-sonnet-4-20250514CC Switch 的配置文件路径通常在~/.cc-switch/config.toml。配置完成后用 CC Switch 切换到 taotoken profile 即可。6. 继续深入Coding Plan 与长期开发建议SkillDeck 从第一行代码到现在全程用 Claude Code 开发。我自己的技术背景是 Java/Go/PythonSwift 之前一行没写过SwiftUI 和 macOS 平台开发零经验。但这次的体验让我感触很深——AI Coding 真的把跨语言开发的门槛拉低了很多。开发节奏基本上就是一个循环提需求、AI 实现、我测试、发现问题、AI 修复、再测试。跟之前用 AI 搓 Skills 的流程差不多但复杂度高了不少毕竟是一个完整的 macOS 桌面应用涉及 UI 布局、文件系统操作、网络请求、并发处理。我不需要先花几周系统学习 Swift 和 SwiftUI遇到不懂的语法或 API 直接问 AI 就行。但这不代表可以完全当甩手掌柜——你得能看懂代码逻辑、能写清楚需求、能有效测试和反馈问题AI 才能帮你持续推进。说白了就是你不需要会写 Swift但你得会验收 Swift 代码。能跑起来、功能正确、边界情况覆盖到这些判断能力还是需要你自己具备。如果你打算长期用 Claude Code 做类似的原生应用开发我建议考虑 Coding Plan。它提供了更稳定的配额和优先级支持适合长期编码和 Agent 开发场景。Coding Plan 地址https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding-planutm_campaignrewrite另外几个实用技巧每个功能新开一个 context不要在一个超长对话里做所有事情完成一个功能后记得 commit这样如果 AI 后续改错了什么可以方便地回滚大量 token 总结的内容保存成文档放到项目的 memory 目录下次开新 context 直接加载用claude --resume恢复历史会话但注意搜索不是百分百精准有时候需要换几个关键词Session 保留策略可以在~/.claude/settings.json里修改cleanupPeriodDays但长期需要保留的内容还是整理成文档更靠谱。SkillDeck 解决的核心痛点就一个让多个 AI Agent 的 Skills 管理更直观易用。从安装、更新、分配到删除全部在一个 GUI 里搞定。如果你也在用多个 AI coding agent被 Skills 管理困扰可以试试这套工程化路径。API 接入文档和更多配置示例可以在这里查看https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite