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 参与你写代码全过程” 的工作方式:

  1. 需求阶段:用自然语言把你想做的功能描述给 ChatGPT,让它帮你拆解任务、规划模块;
  2. 编码阶段:让 ChatGPT 根据你的 SwiftUI / 架构习惯生成示例代码,你再本地调试、组合;
  3. 排错阶段:遇到编译错误、崩溃日志,把完整错误 + 相关代码丢给它,让它给出具体修改建议;
  4. 重构阶段:当项目越来越大,让它帮你做 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 软件与账号

  1. Xcode 26
    在 Mac App Store 安装或更新为最新 Xcode 版本。安装完成后,打开 Xcode → About Xcode 确认版本号是 26.x。

  2. OpenAI 账号 & API Key

    • 注册 / 登录 OpenAI 官网,进入个人设置页面;
    • 创建一个 API Key,复制并妥善保存;
    • 开发阶段可以用环境变量或者本地配置文件保存,切记不要提交到 Git 仓库。
  3. 真机调试证书

    • 使用自己的 Apple ID 登录 Xcode;
    • Signing & Capabilities 打开自动签名;
    • 将 iPhone 插到电脑上,选中设备后可以直接真机运行。

4. 从 0 新建一个 SwiftUI 项目

这一节主要解决:如何搭好一个最基础但干净的 SwiftUI 工程

4.1 用 Xcode 新建 App 工程

  1. 打开 Xcode,选择菜单:File → New → Project…
  2. 在模版列表中选择「iOS → App」,点击 Next。
  3. 在项目配置页填写:
    • Product Name:AIChatDemo(名称随意)
    • Team:选择你登录的 Apple ID
    • Organization Identifier:例如 com.yourname
    • Interface:SwiftUI
    • Language:Swift
    • 勾选项 Use Core Data / Include Tests 可以先都不勾,保持最小工程。
  4. 选择项目存储路径,创建完成后,选中一个模拟器(如 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 有两条路:

  1. 自己写 URLSession + JSON:灵活但样板代码多;
  2. 用社区成熟 SDK:例如 MacPaw/OpenAI,封装好 Chat / Images / Audio 等常用接口。

这里为了教学简单、代码更清晰,我们选用 MacPaw/OpenAI

5.1 添加 Swift 包依赖

  1. 在 Xcode 左侧点击项目根节点;

  2. 顶部切换到「Package Dependencies」;

  3. 点击左下角加号,选择「Add Package Dependency…」;

  4. 在 URL 栏填入:

    https://github.com/MacPaw/OpenAI.git
    
  5. 选择合适的版本规则(一般默认的「Up to Next Major Version」即可),勾选 OpenAI 加入到你的 App Target。

等待 Xcode 完成拉取与解析后,工程左侧会多出一个 OpenAI 包。


6. 实现一个最小可用的 AI Chat 界面(MVVM)

接下来的目标是:做一个最简单但“能用”的聊天界面。

核心模块会分成三块:

  1. 模型层:消息结构 ChatMessage
  2. ViewModel 层:管理消息列表、调用 OpenAI 接口;
  3. 视图层:聊天 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 安全

简单说三条原则:

  1. 不要在 App 二进制里硬编码正式环境的 Key
  2. 优先考虑后端中转,由服务器持有 Key,并做额度、频率限制;
  3. 如果走“用户自带 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. 第 1 阶段:聊天基础

    • 完整实现本文 Demo;
    • 支持多轮对话、消息本地持久化(可以用 SwiftData / CoreData)。
  2. 第 2 阶段:工具化能力

    • 增加“网页总结”“PDF 解读”“翻译”等独立功能页;
    • 每个功能背后写独立的 Prompt 模板 + 调用封装。
  3. 第 3 阶段:统一导航与 UI 体系

    • 用 TabView 或自定义底部导航,把多个功能统一在一个主界面;
    • 设计统一的卡片组件、按钮风格、配色,适配 iOS 26 的新视觉。
  4. 第 4 阶段:账号、订阅与统计

    • 接入自己的后端(可以用 Supabase / 自建服务);
    • 做登录、配额统计、订阅 / 兑换码等商业化能力。
  5. 第 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 各种挑战赛里拿到好成绩!🎉

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐