在 Windows 上开发 iOS App,可以用 SwiftUI 从新建工程写起,分三步做出能增删、勾选并保存数据的待办清单,装到 iPhone 断点调试,再用 TestFlight 或 Ad Hoc 发给同事,全程约一小时。
Windows 开发 iOS App 可以直接从一个空的 SwiftUI 工程起步:装好 KXBuild 和 VS Code 插件,新建工程后分三步写出能增删、勾选并保存到文件的待办清单,按 F5 装到 iPhone 上断点调试,再用 TestFlight 或 Ad Hoc 发给同事。不用装黑苹果,也不用租云 Mac,适合第一次写 iOS 应用的人,全程大约一小时。
开始前准备好三样东西。一个 Apple 开发者账号,用来生成证书和描述文件。一部 iPhone 和能传数据的线。一台装好 KXBuild 和 VS Code 插件的电脑,还没装的按安装页操作。
以下两种做法任选其一。
KXBuild: 新建项目,模板选「iOS 应用(SwiftUI)」,项目名填
MyTodo,Bundle ID 改成你自己的反向域名,例如
com.yourname.mytodo,再选一个存放目录。创建完成后选「在新窗口打开」。D:\projects\MyTodo。1kxbuild project create -t app_swiftui -n MyTodo -b com.yourname.mytodo -o D:\projects

打开后左侧 KXBuild 视图列出 MyTodo 工程和同名的 app target。工程里只有三样东西,
MyTodoApp.swift 是 App 入口,
ContentView.swift 是首页界面,
Assets.xcassets 放图标和颜色。下面的代码全部写在
ContentView.swift 里,省去往工程里添加新文件的步骤。
Bundle ID 是 App 在 Apple 那边的唯一标识,后面生成描述文件、在 App Store Connect 建 App 都要用同一个,现在定好,之后不要再改。
先做一个只能看的列表,数据写死在代码里。把
ContentView.swift 的内容整个换成下面这段。
1import SwiftUI 2 3struct TodoItem: Identifiable, Codable { 4 var id = UUID() 5 var title: String 6 var done = false 7} 8 9struct ContentView: View {10 @State private var items = [11 TodoItem(title: "买牛奶"),12 TodoItem(title: "给 App 换个图标"),13 TodoItem(title: "把 ipa 发给同事", done: true)14 ]1516 var body: some View {17 NavigationView {18 List(items) { item in19 HStack {20 Image(systemName: item.done ? "checkmark.circle.fill" : "circle")21 .foregroundColor(item.done ? .green : .secondary)22 Text(item.title)23 .strikethrough(item.done)24 }25 }26 .navigationTitle("待办")27 }28 }29}
TodoItem 是一条待办的数据,
Identifiable 让
List 能区分每一行,
Codable 留给第三步存文件用。
@State 标记的数组一变,界面就会自动刷新。
写的过程中你会发现,输入
Text(item.title). 之后会弹出
strikethrough、
font、
foregroundColor 这些修饰符,
Image(systemName:) 的参数、
List 的各个初始化方法也都能补全,自己写的类型按 F12 可以跳到定义。
写错的地方会画红线,问题面板里有完整的错误信息。第一次打开 Swift 文件时代码提示要启动几秒,补全不出来时在命令面板运行
KXBuild: 重启代码提示,更多情况见[代码提示与索引]。
第一次在真机上运行要做两件事,放好证书,连好手机。以后每次只需要按 F5。
真机运行需要 Apple Development 证书(.p12)和包含这部手机 UDID 的 Development 描述文件(.mobileprovision)。没有的话可以用 Appuploader 登录开发者账号生成,也可以从 Mac 的钥匙串导出,几种做法见[证书与签名]。手机的 UDID 在连上电脑后运行下面的命令查看,第一列就是。
1kxdevice device list
在工程根目录新建
keychain 文件夹,把两个文件放进去,再新建一个
password.properties,写一行「文件名=密码」。
1development.p12=123456
模板自带的
.gitignore 已经排除了
keychain 目录,证书不会被提交到仓库。
用数据线把 iPhone 接到电脑,在 KXBuild 视图的「设备」节点点一下手机,选为当前设备。第一次连接依次完成这几件事,详见[设备准备]。
按 F5,KXBuild 依次构建、签名、安装、启动 App 并附加调试器,手机上出现写死的三条待办。构建日志在「输出 → KXBuild」,编译错误在问题面板,点一下跳到对应的代码行。
启动失败时通知里会写明卡在哪一步。最常见的是描述文件里没有这部手机的 UDID,把 UDID 加进描述文件、重新下载后替换
keychain 里的旧文件即可。其他提示的处理见[运行与调试]。
现在让列表能改。顶部加一个输入框,点一行切换完成状态,左滑删除,右上角的编辑按钮可以批量删。把
ContentView 换成下面的版本,
TodoItem 保持不变。
1struct ContentView: View { 2 @State private var items: [TodoItem] = [] 3 @State private var newTitle = "" 4 5 var body: some View { 6 NavigationView { 7 List { 8 Section { 9 HStack {10 TextField("新的待办", text: $newTitle)11 .onSubmit(add)12 Button("添加", action: add)13 .disabled(newTitle.trimmingCharacters(in: .whitespaces).isEmpty)14 }15 }16 Section {17 ForEach($items) { $item in18 Button {19 item.done.toggle()20 } label: {21 HStack {22 Image(systemName: item.done ? "checkmark.circle.fill" : "circle")23 .foregroundColor(item.done ? .green : .secondary)24 Text(item.title)25 .strikethrough(item.done)26 .foregroundColor(.primary)27 }28 }29 }30 .onDelete { offsets in31 items.remove(atOffsets: offsets)32 }33 }34 }35 .navigationTitle("待办")36 .toolbar { EditButton() }37 }38 }3940 private func add() {41 let title = newTitle.trimmingCharacters(in: .whitespaces)42 guard !title.isEmpty else { return }43 items.append(TodoItem(title: title))44 newTitle = ""45 }46}
ForEach($items) { $item in … } 拿到的是每一行的绑定,
item.done.toggle() 直接改的就是数组里那一项。
onDelete 同时支持左滑删除和编辑模式下的删除。
改完代码要先停止调试(Shift+F5)再按 F5,调试工具栏上的「重启」不会重新编译。
在
add() 里
items.append(...) 这一行的行号左边点一下,出现红点。回到手机上输入一条待办,点「添加」,App 停住,VS Code 切到调试视图。

左侧「变量」里能看到
title 的值,「调用堆栈」里能看到这次调用是怎么进来的。按 F10 单步执行一行,F5 继续运行。下面的调试控制台可以直接输入 lldb 命令,例如输入
po title 打印当前的标题。代码里的
print 输出显示在「输出 → KXBuild Console」。
现在关掉 App 再打开,待办就没了。用 JSON 文件把列表存到 App 的 Documents 目录,每次改动自动保存,启动时读回来。
把数据和读写逻辑放进一个
TodoStore,界面只管显示。在
ContentView.swift 里
TodoItem 的下面加上这个类。
1final class TodoStore: ObservableObject { 2 @Published var items: [TodoItem] = [] { 3 didSet { save() } 4 } 5 6 private let fileURL = FileManager.default 7 .urls(for: .documentDirectory, in: .userDomainMask)[0] 8 .appendingPathComponent("todos.json") 910 init() {11 if let data = try? Data(contentsOf: fileURL),12 let saved = try? JSONDecoder().decode([TodoItem].self, from: data) {13 items = saved14 }15 }1617 private func save() {18 do {19 let data = try JSONEncoder().encode(items)20 try data.write(to: fileURL, options: .atomic)21 } catch {22 print("保存失败 \(error)")23 }24 }25}
然后改
ContentView 里的四处,把
@State 数组换成这个 store。
1 @StateObject private var store = TodoStore() // 替换 @State private var items2 // …3 ForEach($store.items) { $item in // 原来是 ForEach($items)4 // …5 store.items.remove(atOffsets: offsets) // onDelete 里6 // …7 store.items.append(TodoItem(title: title)) // add() 里
@Published 的数组被修改时会触发
didSet,勾选、删除、添加都会写一次文件。在
init 里给
items 赋值不会触发
didSet,启动读文件时不会反过来又写一遍。
重新按 F5,加几条待办,在手机上把 App 从后台划掉再打开,数据还在。想确认文件确实写了,在
save() 的
let data = … 这行打个断点,勾选一条待办,停住后在调试控制台输入
po fileURL,能看到文件在 App 沙盒里的完整路径。
数据只有几个开关、一两个字符串时,用
@AppStorage("key") 存进 UserDefaults 更省事。待办这种列表数据存成文件更合适,以后想换成 Core Data 或 SwiftData 也好迁移。
手机桌面上 App 的名字默认是 MyTodo。在 KXBuild 视图中右键 target 选「KXBuild: 编辑目标设置」,在 General 页把 Display Name 改成「待办」,Ctrl+S 保存。同一页还能改版本号和最低 iOS 版本,各字段的含义见[工程设置编辑器]。
图标在 KXBuild 视图的资源目录节点里打开
Assets.xcassets,给 AppIcon 放一张 1024×1024 的图,其他尺寸自动生成,见[资源目录编辑器]。改完再按一次 F5 就能在手机上看到。
调试用的 Debug 包只能装在描述文件里列出的手机上。要发给别人装,按人数和对方是否在身边选一种方式。
对方不在身边、人数较多时用 TestFlight。对方在 iPhone 上装 TestFlight App,收到邀请后直接安装,不需要登记 UDID。
在
keychain 目录放入 Apple Distribution 证书和 App Store 类型的描述文件,同样在
password.properties 里加一行密码。然后以下两种做法任选其一。
KXBuild: 选择构建配置),再运行
KXBuild: 构建。1kxbuild build .\MyTodo.xcodeproj -c Release
完成后工程目录下生成
build\MyTodo.ipa。在 App Store Connect 建好这个 App,用 Appuploader 上传 ipa,处理完成后在 TestFlight 页添加测试员,详见[发布与上架]。
几台固定的测试机可以用 Ad Hoc。在开发者网站登记同事手机的 UDID,生成包含这些设备的 Ad Hoc 描述文件,用 Distribution 证书按上面的步骤构建 Release。如果
keychain 里同时放着 App Store 和 Ad Hoc 两个描述文件,构建时用
-D 指定要用的那个。
1kxbuild build .\MyTodo.xcodeproj -c Release -D CODE_SIGN_STYLE=Manual -D CODE_SIGN_IDENTITY="Apple Distribution" -D PROVISIONING_PROFILE_SPECIFIER="MyTodo AdHoc"
把同事的手机接到你的电脑上,执行下面的命令安装。
1kxdevice install .\build\MyTodo.ipa
到这里,一个 Windows 开发 iOS App 的完整流程就走完了:写 SwiftUI 界面、真机断点调试、本地存数据、打 Release 包分发。之后加新页面、接网络请求,都是在同一个工程里重复「改代码、按 F5」这一步。
能。SwiftUI 代码在 Windows 上照常编写、补全和编译,KXBuild 把它构建成能装到 iPhone 的 App。本文的待办清单用到的
List、
NavigationView、
@State、
@StateObject 都是 SwiftUI 的标准写法,换到 Mac 上打开同一个工程也能直接编译。
不能。Debug 包只能装在 Development 描述文件里登记过 UDID 的手机上,别人的手机装不上。要发给别人,用 Apple Distribution 证书打 Release 包,再走 TestFlight 或 Ad Hoc,证书的准备方法见[Windows 给 iOS App 签名]。
调试工具栏上的「重启」不会重新编译。改完 Swift 代码先按 Shift+F5 停止调试,再按 F5,KXBuild 会重新构建、签名并安装到手机。
存在 App 沙盒的 Documents 目录下的
todos.json。卸载 App 时这个目录会一起删除,数据也就没了;只是从后台划掉 App 不影响数据。
来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/70044672/viewspace-3187279/ ,如需转载,请注明出处,否则将追究法律责任。