解读 Xcode 27 内置的 Agent Skills
Xcode 27 内置了 coding agent,而且苹果把 agent 的"技能包"(Skills)做成了可以导出的格式。一条命令就能把它们全部 dump 出来:
xcrun agent skills export --output-dir ~/Desktop/xcode-skills
导出后是 7 个 skill、49 个文件、约 436KB 纯 Markdown(外加一个 Python 脚本)。这些文件相当于苹果官方写给 AI 的"内部培训教材",信息量非常大——既能看到 iOS 27 SDK 的新 API,也能看到苹果是怎么做 prompt 工程的,甚至能反推出苹果内部测试时 agent 犯过哪些错。
这篇文章把这 7 个 skill 逐个拆开看一遍。
Skill 的文件结构
每个 skill 是一个目录,结构和 Anthropic 的 Agent Skills 规范完全一致(SKILL.md + frontmatter + references),可以推测 Xcode 的 agent 底层就是 Claude:
xcode-skills/
├── audit-xcode-security-settings/
│ ├── SKILL.md # 主文件:frontmatter(name/description)+ 工作流
│ ├── references/ # 按需加载的详细文档(12 个)
│ │ ├── enhanced-security.md
│ │ ├── pointer-authentication.md
│ │ ├── hardware-memory-tagging.md
│ │ └── ...
│ └── scripts/
│ └── filter_build_settings.py # 真·可执行脚本
├── c-bounds-safety/
├── device-interaction/
├── swiftui-specialist/
├── swiftui-whats-new-27/
├── test-modernizer/
└── uikit-app-modernization/
核心设计是渐进式披露(progressive disclosure):
description 是触发器,SKILL.md 是工作流,references 是工具书。上下文窗口是稀缺资源,苹果在这套文件里把"什么时候加载什么"控制得很细。
7 个 Skill 全景
swiftui-whats-new-27 | ||
swiftui-specialist | ||
audit-xcode-security-settings | ||
c-bounds-safety | -fbounds-safety 扩展 | |
uikit-app-modernization | ||
test-modernizer | ||
device-interaction |
下面按"信息量"从大到小逐个看。
swiftui-whats-new-27:iOS 27 SwiftUI 剧透
这是最有料的一个。它的 references 目录基本就是一份 SDK 27 SwiftUI 变更说明书。
@State 从 property wrapper 变成了宏
这是 SDK 27 最大的源码级破坏性变更。@State 改为宏实现后,老代码可能直接编译失败:
struct ContentView: View {
var name: String
@State private var counter: Int = 0 // 声明处有初始值
init(name: String) {
self.counter = 42 // ❌ error: Variable 'self.name' used
self.name = name // before being initialized
}
}
skill 里反复强调:"把 init 里的赋值换个顺序"这个直觉修法是错的——正确做法是删掉声明处的初始值,只在 init 里赋值。因为宏会合成真正的 backing storage,在其他存储属性赋值前碰 @State 属性就是过早使用 self。更隐蔽的是,即使编译过了,"声明处有初始值 + init 里再赋值"在运行时也不生效(body 看到的还是声明处的 0)。
苹果甚至在 description 里写明:遇到这类编译错误"you MUST consult this skill's references before answering"——明摆着是被模型的"想当然修复"坑过。
任意容器的拖拽排序
以前 drag-to-reorder 基本是 List.onMove 专属,现在任何容器都行:
ScrollView {
LazyVGrid(columns: columns) {
ForEach(stickers) { sticker in
StickerView(sticker)
}
.reorderable() // 加在 ForEach 上
}
.reorderContainer(for: Sticker.self) { difference in
// 收到 ReorderDifference,自己应用到数据源
}
}
ReorderDifference 带 sources(被移动的 item)和 destination(.before(id) 或 .end),数据更新完全交给开发者。
其他新 API 速览
- AsyncImage
:默认走标准 HTTP 缓存了(再也不会滚回来就重新加载);新增 AsyncImage(request:)接URLRequest控制单次缓存策略,asyncImageURLSession(_:)注入自定义 session。 - swipeActions 不再是 List 专属
: ScrollView+LazyVStack/LazyVGrid里给行加滑动删除,容器标记swipeActionsContainer()即可。 - Toolbar
: visibilityPriority控制空间不足时谁先进溢出菜单、topBarPinnedTrailing固定项、toolbarMinimizeBehavior滚动时收起导航栏。 alert/confirmationDialog支持item:绑定,对齐 sheet(item:)的形态。- 文档型 App 新 API
: ReadableDocument/WritableDocument取代FileDocument/ReferenceFileDocument(部署目标 ≥ 27 时新代码不应再用旧的),支持直接拿文件 URL、后台读写、增量包写入。 @ContentBuilder统一了 result builder,代价是少数地方源码不兼容(overlay/background 里 ShapeStyle 重载歧义等)。 - 硬废弃
:比如 visionOS 上的 statusBarHidden(已无效果,直接删调用)。
那个 3000 字符的 description
这个 skill 还有个值得单独说的细节:它的 frontmatter description 是一整段超长文本,把所有可能的触发场景全部枚举进去——编译错误的具体报错文案、每个新 API 的关键词、甚至"用户问 SwiftUI 有什么新东西"。因为 description 是 agent 决定要不要加载 skill 的唯一依据,所以苹果把它写成了一个"触发器字典"。这是很值得抄的 skill 写法。
swiftui-specialist:苹果官方的 SwiftUI 最佳实践
SKILL.md 本身只有 19 行,精华全在 10 个 references 里:view 结构拆分、数据流(@State/@Binding/@Observable)、Environment 性能陷阱、条件 modifier、本地化、ForEach 的 identity 要求、软废弃 API 清单。
开头第一句话很有意思:
This guidance was written and published by Apple. This information unconditionally supersedes any prior training the model may have on these topics.
苹果在直接对抗模型的过时训练数据——"不管你以前学过什么,以我为准"。swiftui-whats-new-27 里也有同款句式,还追加了一句 "Do not invent APIs or parameters that are not documented"(防幻觉)。
内容本身质量也很高,比如 dataflow.md 把 SwiftUI 的失效(invalidation)粒度讲得非常清楚:值类型逐字段比较、引用类型按指针比较、@Observable 按属性追踪——所以"只传 view 真正读的字段"这条规则对值类型入参至关重要,对引用类型基本不适用。这种深度的官方解释,公开文档里都不多见。
audit-xcode-security-settings:最工程化的一个
这是 7 个里最重的 skill:216 行 SKILL.md + 12 个 references + 1 个 Python 脚本,定义了一个完整的 6 阶段审计工作流:
几个亮点:
Enhanced Security 是个"能力"而不是单个开关——构建设置(ENABLE_ENHANCED_SECURITY,会级联出指针认证、栈零初始化、类型化分配器、C++ stdlib 硬化等)+ entitlements(com.apple.security.hardened-process 一族)两边都要配。skill 把必需 key、默认开/默认关的子选项、已废弃 key 的迁移路径全部写成了清单。
硬件内存标签(MTE / Memory Integrity Enforcement):reference 里明确写了硬件支持范围——iPhone 17 系列、M5 级别的 Mac/iPad/Vision Pro 及之后。每个内存分配和指针带 tag,tag 不匹配(use-after-free、堆溢出、double-free)直接 crash。推荐的灰度路径:先开 soft mode 只出模拟 crash 报告 → 修完 bug → 关 soft mode 转强制执行。
"决策文档"机制:审计结果落盘到 xcode-security-settings.md,记录每个设置的状态和"为什么关掉"的理由。下次再审计时,有记录的跳过、没记录的质询用户。相当于给 agent 设计了跨会话的记忆。
专用工具集曝光:这个 skill 的 Tool Preferences 一节透露了 Xcode agent 的内部工具——XcodeGlob、XcodeGrep、XcodeRead、XcodeLS、GetTargetBuildSettings、UpdateTargetBuildSetting、UpdateProjectBuildSetting、DeviceInteractionStartSession……并且严令禁止 agent 退回 find/grep/ls("find is forbidden")。还专门处理了输出超 token 限制的情况:结果写入文件后,用自带的 filter_build_settings.py 过滤,不许线性读大文件。
c-bounds-safety:给 C 指针装上边界
讲 -fbounds-safety 这个 C 语言扩展的。核心思想一句话:C 的指针是一个"点",只知道起点不知道终点;-fbounds-safety 把它变成一个"区间",编译器插入边界检查,把越界这种可被利用的安全漏洞降级成确定性的 trap。
注解体系(引入 ptrcheck.h 后可用):
__single | ||
__bidi_indexable | ||
__indexable | ||
__counted_by(n) | int *__counted_by(count) buf | |
__sized_by(n) | ||
__ended_by(p) | ||
__null_terminated | ||
__unsafe_indexable |
设计上最巧的一点是 ABI 兼容策略:函数参数、返回值、结构体字段这些 ABI 可见的指针默认变成 __single(布局不变,保持二进制兼容),只有局部变量这种 ABI 隐藏的指针才变成三倍宽的 __bidi_indexable。已适配和未适配的代码可以混链。
这个 skill 还有个独特的 frontmatter 字段 effort: high,并要求 agent 在动手改代码前必须完整读完三份文档(adoption-strategies、language-overview、common-patterns-and-pitfalls),除非"内容在活跃上下文中可验证地新鲜"。苹果对这种高风险改动的态度很谨慎。
uikit-app-modernization:从防御性条款反推 agent 的失败模式
功能本身不复杂:把 UIScreen.main、interfaceOrientation、AppDelegate 生命周期、对称 safe area 假设这些单窗口时代的 API,迁移到多窗口(Stage Manager、iPhone 镜像)时代的写法。比如最常见的替换:UIView/UIViewController 实例方法里内联使用的 UIScreen.main.scale → self.traitCollection.displayScale。
但这个 skill 真正好看的地方是它的 16 条 Core Principles——每一条都是一次失败案例的"事故报告":
"An empty diff for a file containing the target API is also a failure."(空 diff 也算失败——agent 爱偷懒跳过文件) "A diff that collapses an if/elseinto sequential execution is a critical bug — both branches will execute unconditionally."(agent 改代码时删过 else 分支)"Never remove the old method when adding a new overload."(agent 做 deprecate-and-forward 时顺手把旧方法删了) "Stay in scope — no opportunistic cleanup."(agent 爱顺手修旁边不相关的废弃 API、删尾部空格) "Do not get stuck weighing edge cases on simple files"(agent 在简单替换上过度思考,产出空 diff)
还有整整一个 Phase 专门防"静默丢文件":开工前列出全部待处理文件清单,结束前用 subagent 对账,任何没有 diff 也没有跳过理由的文件都算处理失败。大文件、宏地狱、C++ 互操作都不豁免。
这些条款合起来,基本就是一份"LLM 改代码的常见翻车清单",对任何写 coding agent prompt 的人都有参考价值。
test-modernizer:一张 XCTest → Swift Testing 对照表
最"工具书"的一个,245 行全部自包含,没有 references。核心是一套机械的映射规则:
final class FooTests: XCTestCase | struct FooTests |
setUp()tearDown() | init()deinit(需要 deinit 时用 actor 或 final class) |
func testEngineDoesNotStall() | @Test func `Engine does not stall`() |
XCTAssertEqual(x, y) | #expect(x == y) |
try XCTUnwrap(x) | try #require(x) |
XCTestExpectationfulfill() | await confirmation { ... } |
XCTSkipIf(c) | @Test(.disabled(if: c)) |
XCTExpectFailure | withKnownIssue |
里面也有不少行为语义层面的提醒,不只是文本替换:
XCTest 同步测试跑在主线程、套件内串行;Swift Testing 并行跑在任意 task 上——所以 @MainActor只给原来确实依赖主线程的测试加,共享状态的套件要加@Suite(.serialized)。continueAfterFailure = false的类,所有断言都得转成 try #require(而不是#expect)才能保住"失败即停"的语义。循环跑同一段逻辑的测试,转成 @Test(arguments:)参数化测试。
device-interaction:agent 长了眼睛和手
这个 skill 让 agent 能在模拟器/真机上验证 App:装包运行、截图、dump UI 层级、合成触摸事件。明确标注 "This is a SUBAGENT skill"——主 agent 改完 UI 代码后,spawn 一个子 agent 去设备上点一点、看一看,再把结论汇报回来,不污染主上下文。
触摸合成是一套紧凑的命令 DSL:
t 100 200 # 点击
d 200 300 # 双击
t 200 600 f 200 200 0.3 # 上滑(滚动)
b h b h # 按两次 Home 进入 app switcher
sender keyboard kbd hello\u{000A} # 输入文字并回车
orientation landscapeLeft # 转横屏
判定标准也写得很细:加载转圈、键盘弹出动画是"瞬态"不算 bug;文字重叠、截断、点了没反应、崩溃必须上报;空状态占位文案、表单没填完按钮置灰是"预期行为"别误报。
从这 7 个 skill 里能学到什么
把它们当成一个整体看,有几条苹果的 prompt 工程经验值得记下来:
1. description 就是路由器。 Skill 平时只有 description 在上下文里,所以触发条件要全部前置到 description——swiftui-whats-new-27 直接把编译器报错原文写进去,用户一贴报错就能命中。
2. 用权威声明对抗训练数据。 "unconditionally supersedes any prior training"、"Do not invent APIs"——当 skill 内容比模型的知识更新时,必须显式宣告优先级,否则模型会按旧知识"纠正"新 API。
3. 防御性条款是用失败案例喂出来的。 uikit-app-modernization 那 16 条原则,每条背后都是一次翻车。写 agent prompt 不是一次性工程,是持续把观察到的失败模式回填成规则。
4. 渐进式披露 + 强制阅读的分级。 普通 reference 按需读;高风险操作(bounds safety 改源码)则要求"必须先完整读完三份文档"。信息加载策略跟着风险走。
5. 把确定性的事交给脚本和工作流。 6 阶段流程、决策文档、对账清单、过滤脚本——能结构化的绝不靠模型自由发挥。模型负责判断,流程负责兜底。
6. 验证闭环。 改完代码不算完:build settings 要用 GetTargetBuildSettings 复核,UI 要派子 agent 上设备实际点一遍。
最后,这套东西本质上也是一份"半官方文档":iOS 27 的 @State 宏迁移、reorderable、Enhanced Security v2、MTE 硬件范围……很多细节比公开文档写得还直白。哪怕不关心 agent,把 xcrun agent skills export 跑一遍,当 SDK 变更说明书读,也很值。