Swift入门教程:iOS开发第一步
上周五晚上十点半,我瘫在工位上,盯着屏幕上那行红色报错信息发呆:“Missing required module 'BlockchainCore'”。那一刻我真的想砸电脑——不是因为这破错误,而是因为明天就是产品给的deadline,而我这个刚入职两个月的“Swift新手”还在为一个区块链钱包原型 App 跟编译器死磕。
是的,你没看错,区块链。别笑,我们公司最近在搞一个去中心化身份(DID)相关的项目,老板一拍脑袋说要做个 iOS 端的轻量钱包,方便用户管理链上凭证。于是,作为团队里唯一一个“好像会点移动端”的人(其实我只是在大学用 Objective-C 写过一个计算器),这活儿就落我头上了。
说实话,入职前我主要靠 ChatGPT 和 Claude 混日子,写 Python 后端、调 API、搞数据管道,舒服得很。但新公司的技术栈偏 Apple 生态,领导一句“你不是 AI 工具用得挺溜嘛?学 Swift 应该很快”,直接把我送进了坑里。
不过也得感谢这些 AI 编程工具——尤其是 Cursor。试过 GitHub Copilot、CodeWhisperer、甚至国内某大模型的 IDE 插件,最后还是回到了 Cursor。它对上下文的理解特别准,特别是在处理 SwiftUI 的声明式语法时,能根据整个 View 结构推测你要写什么逻辑,而不是像某些工具一样胡乱补全一堆 @State var isLoading = false 这种废话。而且它支持直接用自然语言重构代码,对我这种注重代码可读性和可维护性的人来说简直是救命稻草。
所以,今天这篇《Swift入门教程:iOS开发第一步》,不是那种教科书式的“Hello World”,而是我这两个月踩坑、加班、被产品经理催、被测试提 bug 后的真实复盘。如果你也正准备从零开始搞 iOS 开发,希望我的血泪史能让你少走点弯路。
为什么是 Swift?为什么是现在?
先说清楚,我不是果粉。但我不得不承认,在 Apple 生态里,Swift 是目前最合理的选择。Objective-C?太老了,新人看了想跑。Flutter 或 React Native?可以,但如果你要深度集成 Wallet、Keychain、Face ID 或者以后的 Passkeys,原生 Swift + SwiftUI 几乎是唯一选择。
我们这个项目的需求很明确:做一个轻量级的钱包,能生成密钥对、签名交易、展示 NFT 凭证,并且 UI 要符合 Apple 的 Human Interface Guidelines(HIG)。这意味着不能瞎搞花里胡哨的动画,按钮不能小于 44pt,深色模式必须支持——这些细节在 App Store 审核时都是雷区。
更关键的是,产品经理小王(对,就是那个总在周五下午三点提“小需求”的人)强调:“我们要上架 App Store,而且要快。” 所以跨平台方案直接被否了——审核周期不可控,性能也不如原生。
于是,Swift 成了唯一选项。
环境搭建:Xcode 不是越大越好
很多人以为装个最新版 Xcode 就万事大吉。错!我第一次装的是 Xcode 15.3,结果发现我们依赖的一个区块链 SDK 只支持 Swift 5.8,而 Xcode 15.3 默认用的是 5.9,编译直接报错:
Module compiled with Swift 5.8 cannot be imported by the Swift 5.9 compiler
当时真的裂开。后来才知道,Xcode 其实可以多版本共存。我现在电脑里同时有 Xcode 14.3(用于老项目)和 15.2(用于新项目),通过 xcode-select -s /Applications/Xcode-15.2.app 切换。
建议新手这么做:
- 从 Apple Developer 下载指定版本的 Xcode(需要 Apple ID)
- 重命名为
Xcode-15.2.app避免冲突 - 用命令行切换默认版本
另外,别信网上那些“关闭 SIP 安装旧版 Xcode”的教程,容易把系统搞崩。我同事就这么干过,结果 Mac 无法启动,IT 花了两天才恢复。
第一行代码:别从 Storyboard 开始!
很多教程还在教 Storyboard,拜托,2024 年了!SwiftUI 才是未来。Apple 自己都在 WWDC 上喊了三年 “SwiftUI is the future”。
我们的项目从第一天就决定用纯 SwiftUI。好处太多了:
- 声明式语法,逻辑清晰
- 预览(Preview)功能极强,改完立刻看到效果
- 与 Combine、async/await 天然契合
创建项目时记得勾选 “Use SwiftUI” 和 “Include Tests”。测试?对,我们团队强制要求单元测试覆盖率 > 60%,不然 CI 直接拦住合并。虽然一开始觉得烦,但后来发现真香——特别是处理区块链交易签名这种高风险逻辑时,测试用例救了我好几次。
// ContentView.swift
import SwiftUI
struct ContentView: View {
@State private var walletAddress = "Loading..."
var body: some View {
VStack(spacing: 20) {
Text("My Wallet")
.font(.largeTitle)
.bold()
Text(walletAddress)
.font(.headline)
.foregroundColor(.secondary)
.padding()
.background(Color.gray.opacity(0.1))
.cornerRadius(8)
Button("Generate New Wallet") {
// 这里会调用区块链模块
generateNewWallet()
}
.buttonStyle(.borderedProminent)
}
.padding()
.onAppear {
loadExistingWallet()
}
}
private func generateNewWallet() {
// 实际项目中这里会调用 secp256k1 生成密钥对
// 为了演示,我们模拟一下
walletAddress = "0x" + String(format: "%040x", Int.random(in: 0...Int.max))
}
private func loadExistingWallet() {
// 从 Keychain 读取已有地址
// 如果没有,则显示默认提示
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
这段代码看起来简单,但背后有几个坑:
- Keychain 访问:iOS 对敏感数据存储极其严格。你不能随便把私钥存在 UserDefaults 里,必须用 Keychain。而且 Keychain 在模拟器和真机行为还不完全一致。
- 异步处理:生成密钥对是耗时操作,必须放到后台线程。但 SwiftUI 的
@State只能在主线程更新。这时候就得用Task { }或async/await。 - 预览失效:一旦你引入了第三方库(比如我们的
BlockchainCore),Preview 很可能直接崩溃。解决办法是在 Preview 里 mock 掉依赖。
区块链集成:别在主线程算哈希!
说到 BlockchainCore,这是我们内部封装的一个 Swift Package,基于 secp256k1 和 Web3.swift 改的。产品经理要求“秒开”,但第一次生成密钥对居然要 2 秒!用户肯定以为 App 卡死了。
查了半天才发现,生成椭圆曲线密钥对是 CPU 密集型操作,不能在主线程干。于是赶紧重构:
private func generateNewWallet() {
Task {
let newAddress = await BlockchainManager.shared.generateAddress()
await MainActor.run {
self.walletAddress = newAddress
}
}
}
这里用了 Task + MainActor.run,确保计算在后台,UI 更新回主线程。顺带一提,Cursor 在这一步帮了大忙——我直接选中那段同步代码,右键“Convert to async/await”,它自动生成了正确的并发结构,连 MainActor 都加好了。Claude 虽然也能做到,但经常漏掉线程切换,导致 UI 卡顿。
代码可读性:命名比注释重要
我们团队有个不成文的规定:变量名必须自解释。比如:
❌ let res = api.call()
✅ let userWalletInfo = blockchainAPI.fetchWalletInfo(for: userID)
我知道有些老派程序员觉得这样啰嗦,但当你半夜被 PagerDuty 叫起来修线上 bug 时,你会感谢自己当初的“啰嗦”。
另外,Swift 的 enum + associated values 特别适合表达状态机。比如钱包的状态:
enum WalletState {
case notCreated
case loading
case ready(address: String, balance: Decimal)
case error(NetworkError)
}
配合 SwiftUI 的 switch,UI 逻辑一目了然:
switch walletState {
case .notCreated:
EmptyView()
case .loading:
ProgressView()
case .ready(let address, let balance):
WalletDisplayView(address: address, balance: balance)
case .error(let error):
ErrorView(message: error.localizedDescription)
}
这种写法比一堆 if isLoading && !hasError && address != nil 清晰多了。而且测试时可以直接构造各种状态,不用 mock 网络请求。
App Store 上架:那些没人告诉你的坑
终于开发完了,测试也过了,兴冲冲提交 App Store……然后被拒了。
理由是:“App uses encryption functionality but does not comply with U.S. Export Compliance requirements.”
啥?我们只是用 Secp256k1 生成密钥啊!后来才知道,只要 App 用到了加密算法(哪怕只是 SHA256),就必须在 App Store Connect 里填写出口合规信息。
解决步骤:
- 登录 App Store Connect
- 进入“App 信息” → “出口合规”
- 勾选“此 App 使用了加密”
- 回答几个问题(基本选“No”就行,除非你做军用级加密)
- 提交新的二进制文件
整个过程花了三天。期间产品经理疯狂微信轰炸:“怎么还没上?竞品都上线了!” 我只能苦笑。
另外,隐私清单(Privacy Manifest) 也是新雷区。从 iOS 17 开始,所有第三方 SDK 必须提供 PrivacyInfo.xcprivacy 文件,说明收集哪些数据。我们的 BlockchainCore 本来不收集任何用户数据,但因为用了 CommonCrypto(系统库),也被要求提供清单。最后只好自己写了个空的隐私清单应付审核。
工具链推荐:除了 Cursor,还有谁?
除了 Cursor,我还重度依赖这些工具:
| 工具 | 用途 | 吐槽 |
|---|---|---|
| SwiftLint | 代码风格检查 | 配置太复杂,但团队统一风格必备 |
| Tuist | 项目生成 | 比手写 Project.xcodeproj 稳定多了 |
| Proxyman | 网络调试 | 比 Charles 好用,支持 iOS 17 |
| SimulatorControl | 模拟器管理 | 一键清理/重置,省下无数时间 |
特别提一下 Tuist。Xcode 的 project 文件简直是 Git 冲突制造机。Tuist 用 Swift DSL 定义项目结构,每次构建时生成干净的 .xcodeproj,彻底告别 merge conflict。
最后一点心得
写 Swift 两个月,最大的感受是:Apple 的生态封闭但精致。你得按它的规矩来,否则寸步难行。但一旦适应了,开发体验确实流畅——特别是 SwiftUI + async/await + Property Wrappers 这套组合拳,写起来有种“优雅的克制感”。
至于区块链?说实话,目前 iOS 上的 DApp 体验还很割裂。MetaMask 钱包跳转、签名弹窗权限、后台刷新限制……处处是坑。但这也正是机会所在。如果我们能把钱包做得像 Apple Pay 一样丝滑,说不定真能做出点产品亮点。
哦对了,上周五的那个 bug 最后怎么解决的?原来是我们 BlockchainCore 的 modulemap 没配好。在 Package.swift 里加上:
.target(
name: "BlockchainCore",
dependencies: [],
swiftSettings: [
.unsafeFlags(["-Xcc", "-fmodule-map-file=$(SRCROOT)/BlockchainCore/module.modulemap"])
]
)
搞定。凌晨一点提交 PR,早上九点 CI 通过,十点产品验收——完美。
所以,别怕 Swift。它没那么可怕,尤其当你有 Cursor 这样的 AI 搭档时。Just ship it.
(完)

评论 0