0. 先体验成品:GSearch APP(TestFlight 公测)

在正式开始教程之前,先给你看一个我正在持续迭代的 iOS 应用:GSearch(AI 引导的聚合检索 / 多平台内容搜索 / 效率工具)。

🚀 先体验再看教程,边玩边对照本文,你会更快理解“Cursor + Xcode”的正确分工与迭代方式。

  • ✅ TestFlight 公测链接:https://testflight.apple.com/join/6UF2j2dc
  • ✅ 建议设备:iPhone / iPad(系统版本以 TestFlight 页面要求为准)
  • ✅ 我最想收集的反馈:
    1)搜索速度与稳定性(网络差/切换网络/冷启动)
    2)UI 手感(点击反馈、过渡动画、列表滚动)
    3)平台选择逻辑(你最常用哪几个平台?缺哪个?)
    4)Bug/崩溃/异常提示(截图 + 复现步骤最有用)

1. 为什么我用 Cursor 来做 iOS(SwiftUI)

iOS 开发真正慢的地方,常常不是“打字写代码”,而是:

  • 需求拆解:把一句话变成可执行的 Step(改哪些文件、怎么改)
  • 多文件联动:Model / View / Service / State / Router
  • SwiftUI 结构膨胀:一个 View 写到 600+ 行,编译器开始崩(type-check 报错)
  • Debug:边界条件、线程、状态更新、空态/失败态
  • 上线前:稳定性、隐私合规(Info.plist 权限文案)、回退策略

Cursor 的价值不在“帮你写一段代码”,而在于:
它能在你的规则约束下,执行“计划 → 多文件实现 → 重构 → 测试/文档”的工程化流程。


2. Cursor + Xcode 的最佳分工(不要混用)

结论先放这里:

  • Xcode:编译、运行、签名、真机调试、Archive、TestFlight、Instruments
  • Cursor:写代码、重构、多文件搜索替换、生成测试、整理 Prompt & 规则、写 README/变更说明

你可以把 Cursor 当作“工程协作者”,把 Xcode 当作“构建与发布系统”。


3. 从 0 新建 SwiftUI 工程(最稳路线)

Step 1:用 Xcode 创建工程

1)Xcode → Create a new Xcode project
2)iOS → App
3)Interface: SwiftUI
4)Language: Swift
5)建议勾选 Git(或你自己 init Git)

Step 2:用 Cursor 打开工程目录

在 Cursor 里打开包含 .xcodeproj(或 .xcworkspace)的根目录。

注意:跑起来依旧回 Xcode(Cursor 不负责签名/真机部署/Archive)。


4. 老规矩核心:先立规矩,再让 AI 干活(.cursor/rules)

不立规则,AI 就会:

  • 顺手重命名变量 / 改目录 / 改架构
  • 顺手“大重构”,改完你自己都不敢合并
  • 顺手引入隐私/安全风险(打印敏感信息、硬编码 key)

Step 1:创建规则文件

在仓库根目录创建:.cursor/rules/ios.mdc

把下面内容直接复制进去(后续你可以继续迭代):

# iOS Project Rules (SwiftUI)

- 默认用简体中文输出;技术名词保留英文。
- 不输出“我理解了/好的/没问题”等理解反馈。
- 不做无证据推测:结论必须基于现有代码/日志/截图。
- 最小改动优先:除非明确要求,否则不要大重构、不要改目录结构、不要改命名。
- 修改必须可复制执行:所有代码/命令必须放在代码块里(本文为总览示例)。
- 安全第一:禁止把 token/key 写进仓库;禁止在日志打印敏感信息;网络请求要考虑失败与回退。
- 性能优先:避免 O(n^2);避免 SwiftUI body 超大表达式导致编译慢。
- 如需新增文件:给出“文件路径 + 完整内容”;如修改文件:给出“精确替换片段”。
- 涉及权限/隐私:必须说明 Info.plist Key、权限文案、拒绝后的 UX 回退。

5. Cursor 的正确用法:先计划,再改代码(铁律)

5.1 万能 Prompt(先要计划)

每次改需求,都先用这条:

你是资深 iOS SwiftUI 工程师。请先给出实现计划(Step 1/2/3),列出将修改/新增的文件清单与原因。
约束:
- 不改变现有目录结构与命名
- 不做与需求无关的重构
- 每一步都能编译通过
需求:
(把需求贴这里)

5.2 Debug Prompt(按概率定位根因)

这是 Xcode 报错/日志:(粘贴)
请:
1) 按概率从高到低给出根因列表
2) 给出“最小改动”的修复方案
3) 给出如何防止回归(单测/断言/日志)
约束:不大重构,不改命名。

5.3 SwiftUI 编译慢 / type-check 报错 Prompt

这是 SwiftUI 编译报错与相关代码:(粘贴)
请按“最小改动”修复:
1) 解释根因(不要泛泛而谈)
2) 给出拆分方案(拆哪些 subView / 抽哪些 helper)
3) 给出具体修改后的代码片段
约束:不改命名、不大重构,保证可编译。

6. 实战 Demo:做一个“聚合搜索”最小闭环(可跑起来)

目标:用户选择平台 → 输入关键词 → 点击 Search → 打开对应搜索链接。
(这是聚合检索类 App 的最小闭环,后续扩展聚合结果页/历史记录/收藏/订阅都从这里长出来。)

6.1 新建平台枚举:SearchPlatform.swift

import Foundation

enum SearchPlatform: String, CaseIterable, Identifiable {
    case google
    case youtube
    case reddit
    case bilibili

    var id: String { rawValue }

    var displayName: String {
        switch self {
        case .google: return "Google"
        case .youtube: return "YouTube"
        case .reddit: return "Reddit"
        case .bilibili: return "Bilibili"
        }
    }

    func searchURL(for query: String) -> URL? {
        let q = query.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? query
        let urlString: String

        switch self {
        case .google:
            urlString = "https://www.google.com/search?q=\(q)"
        case .youtube:
            urlString = "https://www.youtube.com/results?search_query=\(q)"
        case .reddit:
            urlString = "https://www.reddit.com/search/?q=\(q)"
        case .bilibili:
            urlString = "https://search.bilibili.com/all?keyword=\(q)"
        }

        return URL(string: urlString)
    }
}

6.2 替换 ContentView.swift(最小 UI + 打开链接)

import SwiftUI

struct ContentView: View {
    @State private var query: String = ""
    @State private var selected: Set<SearchPlatform> = [.google]
    @State private var alertMessage: String?
    @State private var showingAlert: Bool = false

    var body: some View {
        NavigationStack {
            Form {
                Section("关键词") {
                    TextField("输入你要搜索的内容", text: $query)
                        .textInputAutocapitalization(.never)
                        .autocorrectionDisabled(true)
                }

                Section("平台") {
                    ForEach(SearchPlatform.allCases) { p in
                        Toggle(isOn: Binding(
                            get: { selected.contains(p) },
                            set: { isOn in
                                if isOn { selected.insert(p) } else { selected.remove(p) }
                            })
                        ) {
                            Text(p.displayName)
                        }
                    }
                }

                Section {
                    Button {
                        onSearch()
                    } label: {
                        Text("Search")
                            .frame(maxWidth: .infinity, alignment: .center)
                    }
                    .disabled(query.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty || selected.isEmpty)
                }
            }
            .navigationTitle("QuickSearch")
            .alert("提示", isPresented: $showingAlert) {
                Button("OK") {}
            } message: {
                Text(alertMessage ?? "")
            }
        }
    }

    private func onSearch() {
        let q = query.trimmingCharacters(in: .whitespacesAndNewlines)
        guard !q.isEmpty else { return }
        guard !selected.isEmpty else { return }

        // 最简单的行为:打开“第一个选中的平台”
        // 想做更像 GSearch 的体验:结果聚合页/多平台同时出结果/历史记录/订阅策略 → 后续迭代章节再展开
        guard let first = selected.first, let url = first.searchURL(for: q) else {
            alertMessage = "无法生成搜索链接"
            showingAlert = true
            return
        }

        UIApplication.shared.open(url)
    }
}

这一步跑通之后,你已经拥有“可用的最小闭环”。
继续迭代的正确路径:从“打开链接” → “在 App 内展示结果页” → “结果聚合与排序” → “历史与收藏” → “账号与订阅”。


7. 常见坑(Cursor + SwiftUI)一把梭

7.1 “The compiler is unable to type-check this expression in reasonable time”

常见原因:

  • body 太复杂、表达式太长、链式调用太多
  • ForEach/map 里塞了大量逻辑
  • 大量泛型推导导致编译器爆炸

解决套路(工程上最稳):

  • 拆分多个 var xxxView: some View
  • 把复杂计算提到 ViewModel / helper function
  • ForEach 里只做展示,不做重计算

7.2 AI 容易“顺手重构”

压住它的关键句:

  • “不要做与需求无关的重构”
  • “不要改命名/不要动目录结构”
  • “先给文件清单与修改步骤,再给代码”

8. 安全边界(AI IDE 必看)

AI 能加速,也能放大风险。底线规则:

  • 不把真实 token / key / 账号贴给 AI
  • 不在日志打印敏感信息(token、cookie、完整请求头、用户隐私)
  • 所有网络能力都要有失败回退(超时、断网、服务器返回异常)
  • 防 Prompt Injection:仓库里的文档/注释可能被恶意写入“诱导指令”,规则文件必须优先级最高

一句话:AI 可以当快手,但“最终签字的人必须是你”。


9. TestFlight 上线最短路径(实操)

1)Xcode:选择 Release 配置
2)Product → Archive
3)上传到 App Store Connect
4)App Store Connect → TestFlight → 选择构建版本
5)配置测试信息(重点测试点、已知问题、反馈渠道)
6)发邀请链接(Internal/External)


10. 再次招募:GSearch TestFlight 体验官(欢迎来喷)

如果你愿意一起把 GSearch 打磨到“真正好用”,直接加入 TestFlight:

  • ✅ TestFlight 公测链接:https://testflight.apple.com/join/6UF2j2dc

你给我一条高质量反馈(复现步骤 + 截图/录屏),我就能少踩 10 个坑。


参考链接(可选)

  • Cursor 官网:https://cursor.com/
  • Apple SwiftUI 文档:https://developer.apple.com/documentation/swiftui
  • Xcode Release Notes:https://developer.apple.com/documentation/xcode-release-notes/
Logo

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

更多推荐