几个月前我把“用 Copilot 写 SwiftUI”这件事认真推倒重来了一次。之前的方式是打开聊天窗口,把需求说明白,复制粘贴生成的代码,跑一遍发现有语法错误,再修一遍,循环几轮之后人快麻了。后来我把整个流程拆成一套“提供给智能体使用的技能库”,也就是标题里说的 Agent-Skill,效果比想象中好很多——生成代码的可用率、编译通过率,甚至代码风格的稳定性都有明显提升。
这篇文章适合正在用 AI 辅助日常 iOS/macOS 开发、尤其是对 SwiftUI 有实际项目需求的人。如果你正在做“把 AI 生成 SwiftUI 代码稳定落地到工程里”这件事,这篇内容可以直接拿来当一套参考方案。
1. 项目整体设计与思路拆解
1.1 为什么 SwiftUI 项目需要 Agent-Skill 这套东西
SwiftUI 和传统的 UIKit 开发有个很大区别:UIKit 的 API 命名非常直白,tableView(_:numberOfRowsInSection:)这种一看就懂,大模型训练语料里这类代码极其丰富,生成结果不会太离谱。SwiftUI 是声明式 DSL,写法高度依赖链式调用、View 修饰符、ViewBuilder 语法,API 命名简短抽象,同一个效果可以用五种不同的写法实现,但只有一两种符合当前项目场景。大模型在没有上下文约束的情况下,很容易给出“能跑但很丑”的代码,或者调用了已废弃的 API,甚至拼出 ViewerBuilder 与 Group 混用的奇葩结构。
这里就是 Agent-Skill 发挥作用的地方。传统做法里,开发者靠一段 system prompt 告诉 AI“你是 SwiftUI 专家”,实际上这段 prompt 除了让它说话更有信心之外,对代码生成质量几乎没有约束力。Skill 就不一样了,它是由结构化的知识卡片组成的,每一张卡片都对应一类具体的代码生成场景,比如“表单页面生成”“列表与导航结构”“状态管理最佳实践”等,智能体在动手之前必须先检索到对应卡片,再基于卡片里的规则和模板生成代码。
这套东西本质上做的是两件事:一是把 SwiftUI 的开发经验从人脑里搬到可检索的外部知识库中;二是让智能体在执行生成任务时,每一步都受技能规则约束,而不是靠大模型的概率自由发挥。
1.2 “Agent + Skill” 和 “直接写 Prompt” 的本质差异
很多人觉得用 AI 生成代码就是靠提示词,提示词写得好结果就好。实际试过就知道,这个认知只对了一半。提示词能决定智能体“做什么”,但很难决定它“怎么做对”。比如你告诉它“生成一个编辑用户信息的表单页面”,它大概率会生成一个标准的Form,看起来没毛病,但你的项目里所有表单都有统一的背景色、统一的圆角风格、统一的校验逻辑,这些项目级约束靠一段聊天提示词根本塞不下。
Skill 系统的价值在它的流程设计:需求进来之后,先做意图识别,把“编辑用户信息的表单页面”转成结构化的任务描述;然后做技能检索,从技能库里选出“表单生成技能”“用户信息模型处理技能”“错误提示技能”等相关卡片;最后才是代码生成,并且生成过程中要求严格遵循卡片里的规则和代码模板。
有一家做低代码平台的团队分享过他们的方案,他们把生成过程分成三步:意图理解 → 技术选型 → 代码生成。意图理解层负责从自然语言里抽取关键要素,技术选型层根据项目上下文决定用NavigationStack还是旧的NavigationView,代码生成层才让大模型动手写代码。这套分层思路我很认同,Agent-Skill 本身就是这个思想的落地:Skill 卡片不仅包含代码模板,还包含“什么时候用这个方案”“什么时候不要用这个方案”的判断标准,把技术选型从大模型的隐性知识变成了显性规则。
2. 核心细节解析与实操要点
2.1 SwiftUI 声明式特性对 Skill 卡片的特殊要求
SwiftUI 和 UIKit 的底层思维模式不同,Skill 卡片的编写方式也必须跟着调整。UIKit 时代,代码是命令式的:“创建一个列表视图,设置数据源,注册 cell 类型,刷新数据”。到了 SwiftUI,代码是描述式的:“这个视图在数据为某个状态时显示什么”。这两种不同范式决定了 Skill 卡片的核心不是记住 API 名字,而是记住“什么状态对应什么视图输出”。
我的技能库里专门有一类卡片,叫“状态映射卡”,记录的就是这种对应关系。比如网络请求页面的状态机通常有四态:加载中、成功、失败、空数据。对应到 SwiftUI 上,加载中的标准做法是用ProgressView配合.overlay或者直接切换主视图内容,成功态是用List或ScrollView展示数据,失败态是显示提示文案加重试按钮,空数据态则是自定义的占位视图。这些映射关系如果不沉淀在技能卡里,每次让 AI 写页面,它都会随机发挥,可能这次用.overlay,下次直接改body,代码风格完全不稳定。
还有一个重要细节是@State、@ObservedObject、@EnvironmentObject的选择逻辑。这是 SwiftUI 新手最容易写错的点,也是 AI 最容易犯的错误。技能卡里必须明确写好判断标准:只有这个视图自己持有的临时状态用@State;从父视图传入、需要监听变化的模型对象用@ObservedObject;多个子视图共享的全局状态用@EnvironmentObject配合.environment()注入。这套规范在卡片里写清楚之后,AI 生成的代码里@State滥用的情况大幅减少。
2.2 一份高质量的 SwiftUI 技能卡必须包含哪些模块
写技能卡最忌讳的就是写一堆“SwiftUI 是苹果的 UI 框架,用于构建用户界面”这种废话。技能卡的信息密度要高,每一条都要能直接指导代码生成。以我维护的“表单页面技能卡”为例,它包含六个必需模块。
第一是触发场景描述,明确什么时候应该选中这张卡:当用户需求里出现“表单”“提交”“编辑资料”“录入信息”等关键词时,优先使用本技能。第二是禁止事项,比如“不要用Form嵌套ScrollView”“不要把多个TextField直接并列放在同一行”“提交按钮不要放在Form外部的VStack里”,这些禁止事项是从实际项目踩坑里总结出来的。第三是基础代码骨架,给出一个可以直接套用的最小可编译模板。第四是风格变量定义,比如输入框高度统一为 44pt、圆角统一为 10pt、背景色统一用Color(.systemGroupedBackground)。第五是数据流建议,比如“表单数据建议用@StateObject持有一个ObservableObject模型,而不是大量使用独立的@State”。第六是校验逻辑实现方式,比如“提交前用validate()方法统一校验,错误提示用Section头部的Text展示”。
这六个模块缺哪个,生成结果都会出问题。没有禁止事项,AI 就会写出嵌套结构;没有风格变量,每次生成的页面观感都不一样;没有数据流建议,写着写着就变成了几十个@State堆积在一起。
2.3 跨平台差异必须刻进 Skill 卡
SwiftUI 最大的卖点之一是一套代码跑通苹果全平台,但实际开发中,跨平台移植的坑比想象中多得多。同一套代码在 iOS 上完全正常,到了 macOS 上布局完全错乱,原因是两边的默认渲染机制和窗口尺寸差异很大。
Skill 卡片里需要把平台差异固化成规则。比如List在 iOS 上是平铺的滚动列表,在 macOS 上默认是带分组背景的侧边栏风格,如果目标是双端统一,代码里必须显式设置.listStyle(.plain)或自定义平台判断。再比如NavigationView在 iOS 上默认是推入式导航,在 macOS 上默认是主从结构的侧边栏导航,这两种交互模式拿到同一份布局代码里,行为完全不同。所以我在技能库里维护了一张“导航结构平台差异卡”,iOS 场景默认使用NavigationStack,macOS 场景需要开发者明确告知期望的导航形态。这张卡的价值在产品要出 Mac 版本的那天才能真正体会——如果没有这个东西,AI 生成的 macOS 界面基本就是 iOS 视图硬拉伸,可用度很低。
3. 实操过程与核心环节实现
3.1 技能库的整体目录结构与组织方式
技能库的文件组织直接决定检索效果。我采用的是分层目录结构:顶层按功能域分,比如list-and-navigation、form-and-input、state-management、>