用 ChatGPT「Codex 工作流」从 0 到 1 开发 iOS 26 AI APP(附 GSearch 内测体验)
本文以我自研的 GSearch APP 为例,系统讲解如何在 iOS 26 环境下用 SwiftUI + OpenAI 搭建一款可落地的 AI 聊天应用,从新建工程、集成 SDK 到实现对话界面与调用流程,并结合 Agentic Coding 思路示范如何让 ChatGPT 参与需求拆解、代码生成与重构,帮助读者快速搭建自己的智能 App。
用 ChatGPT「Codex 工作流」从 0 到 1 开发 iOS 26 AI APP(附 GSearch 内测体验
- 0. 先体验一下成品:GSearch APP(TestFlight 内测)
- 1. 本文定位 & 适用场景
- 2. 什么是「ChatGPT Codex 工作流」?
- 3. 环境准备:Xcode 26 + OpenAI 账号 + 真机
- 4. 从 0 新建一个 SwiftUI 项目
- 5. 通过 Swift Package Manager 接入 OpenAI Swift SDK
- 6. 实现一个最小可用的 AI Chat 界面(MVVM)
- 7. 如何高效地用 ChatGPT 来写/改这些代码?
- 8. 安全、费用与扩展:真实项目必须考虑的点
- 9. 从 Demo 到 GSearch 级产品:可能的进阶路线
- 10. 结语:挑战赛的文章怎么继续写?
0. 先体验一下成品:GSearch APP(TestFlight 内测)
在正式讲教程之前,先给你看一下我自己用 ChatGPT「Codex 工作流」辅助开发的实战作品 —— GSearch APP。
这是一个面向 iOS 26 的 AI 工具类 App,集成了多种搜索与 AI 能力,UI 适配了最新系统效果,非常适合当做你学习本教程时的“参考终局”。
👉 TestFlight 内测链接(欢迎体验 & 提建议):
https://testflight.apple.com/join/6UF2j2dc
你可以一边玩 GSearch,一边对照本文的步骤,看清楚:
“从一个空工程,到像 GSearch 这样可以真实使用的 APP,中间到底要走哪些关键步骤?”
1. 本文定位 & 适用场景
这篇文章按照 CSDN 创作挑战赛的要求来写,内容尽量做到:
- 故事线完整:从 0 新建工程,一路走到能跑的 AI 聊天界面;
- 代码可直接跑:用最新的 iOS 26 + Xcode 26 思路,不再讲过时的 UIKit Demo;
- 配合 ChatGPT 使用:不仅教你“写什么代码”,还重点讲“怎么给 ChatGPT 下指令,才能帮你写出好代码”。
适合人群:
- 已经会一点 Swift / SwiftUI,想趁着 CSDN 挑战赛系统输出一个 AI 项目;
- 正在开发 iOS APP,想要把 ChatGPT 当成“Swift 高级工程师 + 架构师”来协助;
- 想了解像 GSearch 这种“多工具聚合 + AI 中枢”的产品,从技术角度是怎么落地的。
2. 什么是「ChatGPT Codex 工作流」?
这里的“Codex”不是指某一个旧模型,而是一整套 “让 ChatGPT 参与你写代码全过程” 的工作方式:
- 需求阶段:用自然语言把你想做的功能描述给 ChatGPT,让它帮你拆解任务、规划模块;
- 编码阶段:让 ChatGPT 根据你的 SwiftUI / 架构习惯生成示例代码,你再本地调试、组合;
- 排错阶段:遇到编译错误、崩溃日志,把完整错误 + 相关代码丢给它,让它给出具体修改建议;
- 重构阶段:当项目越来越大,让它帮你做 MVVM 拆分、提取组件、统一命名和注释。
你要做的事只剩下两件:
- 讲清楚需求;
- 审核它给的方案和代码。
为了让这套工作流稳定运行,我们需要先给 ChatGPT 定义一个“角色”。
2.1 固定一个「项目专用对话」和角色 Prompt
建议你在 ChatGPT(网页版或桌面端)中专门新建一个会话,标题可以写成:
ChatGPT 5.1 · iOS 26 Codex 助手
然后在这个会话里粘贴下面这段 角色设定 Prompt,以后所有的开发问题都在这一个会话里进行:
你现在是我的结对编程伙伴,角色是:
- 10 年以上经验的 iOS / Swift / SwiftUI 高级工程师
- 熟悉 Xcode 26 和 iOS 26 新特性
- 熟悉 OpenAI API 以及常用的 Swift SDK(如 MacPaw/OpenAI)
- 熟悉 MVVM 架构、异步编程(async/await)、安全与成本控制
开发规则:
1. 尽量给出完整、可编译的代码示例(包括 import、类型定义等),不要省略关键部分。
2. 当我贴出现有代码时,只在此基础上做“增量修改”,不要无故重写不相关的部分。
3. 如果你发现我的写法会造成安全、性能或费用问题,请在代码后用中文指出。
4. 所有英文技术名词用英文,解释用中文。
接下来我会从 0 开始做一个 SwiftUI iOS App,你先记住这些规则,不用回答。
这样 ChatGPT 在后续对话里就会默认以“iOS 高工”的视角来回应你了。
3. 环境准备:Xcode 26 + OpenAI 账号 + 真机
为了保证教程尽可能“未来可用”,这里统一按照 iOS 26 + Xcode 26 来写。
3.1 硬件与系统要求
- Mac 一台(推荐 Apple Silicon:M1 / M2 / M3 / M4 系列);
- 系统建议为最新稳定版 macOS;
- iPhone 一台(最好已经升级到 iOS 26,方便体验新特性);
- 带宽稳定的网络(访问 OpenAI 接口时会更顺畅)。
3.2 软件与账号
-
Xcode 26
在 Mac App Store 安装或更新为最新 Xcode 版本。安装完成后,打开Xcode → About Xcode确认版本号是 26.x。 -
OpenAI 账号 & API Key
- 注册 / 登录 OpenAI 官网,进入个人设置页面;
- 创建一个 API Key,复制并妥善保存;
- 开发阶段可以用环境变量或者本地配置文件保存,切记不要提交到 Git 仓库。
-
真机调试证书
- 使用自己的 Apple ID 登录 Xcode;
- 在
Signing & Capabilities打开自动签名; - 将 iPhone 插到电脑上,选中设备后可以直接真机运行。
4. 从 0 新建一个 SwiftUI 项目
这一节主要解决:如何搭好一个最基础但干净的 SwiftUI 工程。
4.1 用 Xcode 新建 App 工程
- 打开 Xcode,选择菜单:File → New → Project…
- 在模版列表中选择「iOS → App」,点击 Next。
- 在项目配置页填写:
- Product Name:AIChatDemo(名称随意)
- Team:选择你登录的 Apple ID
- Organization Identifier:例如 com.yourname
- Interface:SwiftUI
- Language:Swift
- 勾选项 Use Core Data / Include Tests 可以先都不勾,保持最小工程。
- 选择项目存储路径,创建完成后,选中一个模拟器(如 iPhone 16 Pro),按下 ⌘+R 运行,确保空壳 App 可以正常启动。
4.2 项目基本设置
在左侧选中项目 -> Target「AIChatDemo」:
- 在 General 面板中,将 Deployment Target 设置成 iOS 18 或 19,如果你只打算支持 iOS 26 新设备,也可以直接拉到 26;
- 在 Signing & Capabilities 中,确认 Team、Bundle Identifier 正常无报错,勾选「Automatically manage signing」。
至此,一个纯净 SwiftUI 工程就建立好了。
5. 通过 Swift Package Manager 接入 OpenAI Swift SDK
在 iOS 上调用 OpenAI API 有两条路:
- 自己写 URLSession + JSON:灵活但样板代码多;
- 用社区成熟 SDK:例如 MacPaw/OpenAI,封装好 Chat / Images / Audio 等常用接口。
这里为了教学简单、代码更清晰,我们选用 MacPaw/OpenAI。
5.1 添加 Swift 包依赖
-
在 Xcode 左侧点击项目根节点;
-
顶部切换到「Package Dependencies」;
-
点击左下角加号,选择「Add Package Dependency…」;
-
在 URL 栏填入:
https://github.com/MacPaw/OpenAI.git -
选择合适的版本规则(一般默认的「Up to Next Major Version」即可),勾选
OpenAI加入到你的 App Target。
等待 Xcode 完成拉取与解析后,工程左侧会多出一个 OpenAI 包。
6. 实现一个最小可用的 AI Chat 界面(MVVM)
接下来的目标是:做一个最简单但“能用”的聊天界面。
核心模块会分成三块:
- 模型层:消息结构
ChatMessage; - ViewModel 层:管理消息列表、调用 OpenAI 接口;
- 视图层:聊天 UI(气泡 + 输入框)。
6.1 定义聊天消息模型
在项目中新建文件 ChatModels.swift,填入以下内容:
import Foundation
/// 消息角色:用户 or 助手
enum MessageRole {
case user
case assistant
}
/// 聊天消息
struct ChatMessage: Identifiable, Hashable {
let id = UUID()
let role: MessageRole
let text: String
let createdAt: Date
init(role: MessageRole, text: String, createdAt: Date = Date()) {
self.role = role
self.text = text
self.createdAt = createdAt
}
}
extension ChatMessage {
static func user(_ text: String) -> ChatMessage {
ChatMessage(role: .user, text: text)
}
static func assistant(_ text: String) -> ChatMessage {
ChatMessage(role: .assistant, text: text)
}
}
6.2 编写 ViewModel:封装 OpenAI 调用
新建 ChatViewModel.swift:
import Foundation
import SwiftUI
import OpenAI
@MainActor
final class ChatViewModel: ObservableObject {
// MARK: - 状态
@Published var messages: [ChatMessage] = [
.assistant("你好,我是内置的 AI 助手,有什么想问我的吗?")
]
@Published var inputText: String = ""
@Published var isSending: Bool = false
@Published var errorMessage: String?
private let client: OpenAI
// MARK: - 初始化
init() {
// ⚠️ 开发阶段示例:从环境变量中读取 API Key
// 真实项目建议放在服务器中转,或由用户自行输入
let apiKey = ProcessInfo.processInfo.environment["OPENAI_API_KEY"] ?? ""
self.client = OpenAI(apiToken: apiKey)
}
// MARK: - 对外方法:发送一条消息
func sendMessage() async {
let trimmed = inputText.trimmingCharacters(in: .whitespacesAndNewlines)
guard !trimmed.isEmpty, !isSending else { return }
// 1. 先把用户消息加入本地列表
let userMessage = ChatMessage.user(trimmed)
messages.append(userMessage)
inputText = ""
errorMessage = nil
isSending = true
do {
// 2. 把历史消息转成 OpenAI 所需格式
let history: [ChatQuery.ChatCompletionMessageParam] = messages.map { msg in
switch msg.role {
case .user:
return .user(.init(content: .string(msg.text)))
case .assistant:
return .assistant(.init(content: .string(msg.text)))
}
}
// 3. 构建请求,这里用一个性价比很高的模型(示例名,以实测可用为准)
let query = ChatQuery(
messages: history,
model: .gpt4_1_mini,
temperature: 0.7
)
// 4. 调用聊天接口
let result = try await client.chats(query: query)
// 5. 简化逻辑:取第一条回复文本
if let reply = result.choices.first {
let text = reply.message.content?.string ?? "(模型未返回文本内容)"
messages.append(.assistant(text))
} else {
messages.append(.assistant("没有拿到模型返回内容,请稍后重试。"))
}
} catch {
messages.append(.assistant("调用 OpenAI 出错:\(error.localizedDescription)"))
errorMessage = error.localizedDescription
}
isSending = false
}
}
说明:
@MainActor保证所有 UI 相关状态更新都在主线程执行;client初始化时从环境变量拿 Key,方便在终端里临时配置;- 这里只做了“完整历史对话”模式,后面可以扩展为“摘要历史”以节省 Token。
6.3 编写 SwiftUI 界面:聊天气泡 + 输入框
新建 ChatView.swift:
import SwiftUI
struct ChatView: View {
@StateObject private var viewModel = ChatViewModel()
var body: some View {
NavigationStack {
VStack(spacing: 0) {
messagesList
Divider()
inputBar
}
.navigationTitle("AI Chat Demo")
}
}
// MARK: - 消息列表
private var messagesList: some View {
ScrollViewReader { proxy in
ScrollView {
LazyVStack(spacing: 12) {
ForEach(viewModel.messages) { message in
messageRow(message)
.id(message.id)
}
}
.padding(.horizontal)
.padding(.vertical, 8)
}
.background(Color(.systemGroupedBackground))
.onChange(of: viewModel.messages.count) { _ in
// 新消息时滚动到底部
if let lastID = viewModel.messages.last?.id {
withAnimation {
proxy.scrollTo(lastID, anchor: .bottom)
}
}
}
}
}
// MARK: - 单条消息气泡
@ViewBuilder
private func messageRow(_ message: ChatMessage) -> some View {
HStack {
if message.role == .assistant {
bubble(text: message.text, isUser: false)
Spacer(minLength: 32)
} else {
Spacer(minLength: 32)
bubble(text: message.text, isUser: true)
}
}
}
private func bubble(text: String, isUser: Bool) -> some View {
Text(text)
.padding(10)
.foregroundColor(isUser ? .white : .primary)
.background(
RoundedRectangle(cornerRadius: 16, style: .continuous)
.fill(isUser ? Color.accentColor : Color(.secondarySystemBackground))
)
.frame(maxWidth: UIScreen.main.bounds.width * 0.75,
alignment: isUser ? .trailing : .leading)
}
// MARK: - 底部输入栏
private var inputBar: some View {
HStack(alignment: .bottom, spacing: 8) {
TextEditor(text: $viewModel.inputText)
.frame(minHeight: 36, maxHeight: 100)
.padding(6)
.background(
RoundedRectangle(cornerRadius: 12)
.fill(Color(.secondarySystemBackground))
)
Button {
Task {
await viewModel.sendMessage()
}
} label: {
if viewModel.isSending {
ProgressView()
} else {
Image(systemName: "paperplane.fill")
.font(.system(size: 18))
}
}
.disabled(viewModel.isSending || viewModel.inputText.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty)
}
.padding(.horizontal)
.padding(.vertical, 8)
.background(.ultraThinMaterial)
}
}
最后,把项目的入口改成 ChatView。打开 AIChatDemoApp.swift,修改为:
import SwiftUI
@main
struct AIChatDemoApp: App {
var body: some Scene {
WindowGroup {
ChatView()
}
}
}
现在给环境变量设置上你的 API Key(例如在 Xcode 的 Run Scheme 里设置 OPENAI_API_KEY),跑一下,基本就能看到一个可以和 ChatGPT 对话的 iOS 26 聊天 APP 了。
7. 如何高效地用 ChatGPT 来写/改这些代码?
上面是“结果”,这节讲“方法论”——实际开发时,你可以怎么把 ChatGPT 当成 Xcode 插件一样用。
7.1 让它先帮你“画蓝图”
当你有了一个大概想法(比如:做一个类似 GSearch 的 AI 工具 App),可以这样对它说:
我想用 SwiftUI 做一个 iOS 26 App,主功能有:
- 和 OpenAI 聊天
- 后续会增加:网页总结、PDF 分析、多搜索引擎聚合查询等
请你先帮我:
1. 用文字画出推荐的项目结构(分层:View / ViewModel / Service)。
2. 列出每一层建议有哪些 Swift 文件、负责什么。
3. 指出哪些部分最适合先做成“最小可用版本”(MVP)。
它给出结构之后,你就可以按模块一个一个落地,避免一上来就写成“巨石工程”。
7.2 编译错误 / 崩溃时的求助模板
当你遇到编译错误,不要只丢一句“帮我看看错在哪”,可以用这样的格式:
我在 Xcode 26 上编译时报错,错误信息如下(完整粘贴):
<错误信息>
这是我当前的 ChatViewModel.swift 全部代码:
// 这里粘完整文件
请你:
1. 指出导致错误的根本原因是什么;
2. 给出修改后的完整 ChatViewModel.swift;
3. 用列表说明你改动了哪些地方、为什么要这么改。
这样 ChatGPT 能同时看到“错误 + 上下文”,给出的答案会比只看到一行错误准确得多。
7.3 让 ChatGPT 帮你重构 & 抽组件
当你的 View 逐渐变复杂,可以直接把整份 View 贴给它,说:
下面是我目前的 ChatView.swift,逻辑有点多了。
请你帮我做两件事:
1. 把它重构成多个小 View(例如:消息列表、单条消息气泡、输入栏等)。
2. 提出一个简单的 MVVM 分层方案,并给出对应的 ViewModel 草图代码。
并加一条限制:
请尽量复用我当前的变量名和 UI 风格,只做必要的拆分,不要大改结构。
这样就能在保持原有风格的前提下做系统性重构。
8. 安全、费用与扩展:真实项目必须考虑的点
Demo 阶段你可以“跑起来就好”,但当你要把 App 做成像 GSearch 那样的长期产品,就需要考虑更多问题。
8.1 API Key 安全
简单说三条原则:
- 不要在 App 二进制里硬编码正式环境的 Key;
- 优先考虑后端中转,由服务器持有 Key,并做额度、频率限制;
- 如果走“用户自带 Key”模式,要在 UI 里写清楚费用由用户自己承担,并保护好用户本地存储。
文章中示例用的是环境变量,只适合本地开发 / 内网演示。
8.2 费用控制:模型选择与上下文长度
- 小模型(例如 mini / small 类)非常适合在移动端做日常对话;
- 尽量控制单次回答长度,避免模型“长篇大论”浪费 Token;
- 对长期会话可以定期做“摘要压缩”,把旧消息总结成一段,减小上下文长度。
你完全可以写一个 TokenBudgetManager,专门负责计算本次调用可能花费的 Token,并在 UI 中给用户展示一个简易的“使用情况”。
8.3 多模型与多工具扩展
当你完成了本文的基础 Demo,就可以尝试:
- 为不同场景选择不同的模型(聊天、写代码、生成总结等);
- 接入更多“工具(Tools)”:比如网页抓取、搜索引擎 API、知识库检索;
- 封装一个统一的
AIEngine协议,底层可以接 OpenAI、本地模型或其它厂商。
GSearch 正是在这个方向上不断扩展:从最初的“单一聊天”,走向“多搜索引擎 + 多 AI 能力聚合”。
9. 从 Demo 到 GSearch 级产品:可能的进阶路线
如果你想沿着本文的 Demo 一直做下去,最终做成一个像 GSearch 那样可以公开给用户使用的 App,大致可以按这个路线走:
-
第 1 阶段:聊天基础
- 完整实现本文 Demo;
- 支持多轮对话、消息本地持久化(可以用 SwiftData / CoreData)。
-
第 2 阶段:工具化能力
- 增加“网页总结”“PDF 解读”“翻译”等独立功能页;
- 每个功能背后写独立的 Prompt 模板 + 调用封装。
-
第 3 阶段:统一导航与 UI 体系
- 用 TabView 或自定义底部导航,把多个功能统一在一个主界面;
- 设计统一的卡片组件、按钮风格、配色,适配 iOS 26 的新视觉。
-
第 4 阶段:账号、订阅与统计
- 接入自己的后端(可以用 Supabase / 自建服务);
- 做登录、配额统计、订阅 / 兑换码等商业化能力。
-
第 5 阶段:上 TestFlight 与迭代
- 和我一样,把 App 发到 TestFlight,找一批种子用户体验;
- 收集日志和反馈,按用户真实需求继续打磨。
如果你已经看完这篇文章并跑通 Demo,你其实已经站在了第 1 阶段的门口 —— 后面就是不断“加功能 + 做体验 + 控制成本”的系统工程。
10. 结语:挑战赛的文章怎么继续写?
为了在 CSDN 的创作挑战赛里更有竞争力,你可以把本文当成“系列的第一篇”,后面继续扩展:
- 第 2 篇:《把聊天历史存下来:SwiftData 持久化 AI 对话全流程》
- 第 3 篇:《让 iOS App 懂图片:给你的 AI Chat 增加图像理解能力》
- 第 4 篇:《从 Demo 到产品:为 iOS AI APP 接入安全的 OpenAI 代理后端》
- 第 5 篇:《GSearch 架构拆解:一个 iOS 26 AI 工具 App 的真实形态》
每篇文章都可以围绕“ChatGPT + SwiftUI + iOS 26”这个主线展开,这样:
- 你能系统整理自己的开发过程;
- 读者能跟着你的节奏一步步做出自己的 AI APP;
- 你的 GSearch APP 也会在这个过程中不断被更多开发者看到。
最后再放一次 GSearch 内测入口,欢迎你一边读一边体验,也欢迎在评论区告诉我你是怎么用本文的代码的:
👉 GSearch TestFlight 内测链接:
https://testflight.apple.com/join/6UF2j2dc
祝你写得开心,码得顺利,也祝你在 CSDN 各种挑战赛里拿到好成绩!🎉
更多推荐


所有评论(0)