Skip to content

iOS 开发

面向有一定编程基础、想系统掌握 iOS 开发的笔记。iOS 开发的主线是 Swift 语言 + SwiftUI(或 UIKit)+ 系统能力(并发/存储/网络),副线是签名、打包与上架流程。示例基于 Swift 6 与 iOS 26 SDK 语境编写,需在 Xcode 中验证。

Swift 语言要点(够用的核心)

Swift 是 iOS 的第一公民语言,先建立四个心智:

  • 值类型优先struct 默认拷贝,class 引用共享。写 UI 状态模型优先用 struct,需要共享生命周期再考虑 class;
  • 可选与解包Optional 是第一等类型,? 声明、if let/guard let 解包、?? 给默认值。避免裸 ! 强解包;
  • 协议与扩展:协议 = 接口 + 约束,扩展 = 给既有类型加方法。SwiftUI 大量依赖「协议 + 修饰符 + 泛型」组合;
  • 并发模型async/await 是主线,actor 保护共享可变状态。Swift 6 严格并发下,跨线程传非 Sendable 数据是编译错误而非运行时事故。

一个最小数据模型 + 网络调用示例:

swift
struct User: Codable, Identifiable {
    let id: Int
    let name: String
}

actor UserStore {
    private(set) var users: [User] = []

    func load() async throws {
        let url = URL(string: "https://api.example.com/users")!
        let (data, _) = try await URLSession.shared.data(from: url)
        users = try JSONDecoder().decode([User].self, from: data)
    }
}

要点:URLSession.data(from:) 是 async 版本,不用闭包回调;模型 Codable 直接映射 JSON;共享可变状态用 actor 收口,天然串行。

Xcode 工程与依赖

  • 工程结构.xcodeproj 管理 target/签名/构建配置;建议新建工程选 SwiftUI App 模板 + Git;
  • 依赖管理:Swift Package Manager(SPM)是官方默认,支持从 Xcode 直接添加包;CocoaPods 仅在老项目/个别私有库场景需要;
  • 签名:调试用个人/团队自动签名即可;发布签名见打包、签名与发布

App 生命周期

iOS 应用围绕「前台/后台」切换管理资源:

状态含义你要做的
Not running未运行
Inactive过渡态(来电、控制中心)短暂状态,少做重活
Active前台运行正常业务
Background退后台保存状态、停止重活、申请后台任务时间
Suspended挂起(随时可被杀)数据应先于此时保存

SwiftUI 里用 scenePhase 感知生命周期:

swift
@Environment(\.scenePhase) private var scenePhase

var body: some Scene {
    WindowGroup {
        ContentView()
            .onChange(of: scenePhase) { _, phase in
                switch phase {
                case .background: saveDraft()
                default: break
                }
            }
    }
}

核心纪律:不要在退后台时才想起保存用户数据——每次都写,而不是在生命周期回调里补。

SwiftUI:状态驱动 UI

SwiftUI 的范式是「UI = f(状态)」:

  • @State 管理视图私有值;@Bindable/$ 双向绑定;
  • @Observable 宏 + @Environment 做跨视图共享(iOS 17+ 替代 ObservableObject);
  • 视图 body 是声明,SwiftUI 对比旧状态做增量更新;
  • 列表与懒加载用 LazyVStack/List,避免一次性构建大量视图。
swift
@Observable
final class CartModel {
    var items: [String] = []
    var total = 0

    func add(_ name: String) { items.append(name); total += 1 }
}

struct CartView: View {
    @State private var cart = CartModel()

    var body: some View {
        VStack {
            Text("共 \(cart.total) 件")
            List(cart.items, id: \.self) { Text($0) }
        }
        .task {
            // 视图出现时加载
        }
    }
}

注意:body 里不要写副作用;数据加载放 .task 或显式触发点,网络/IO 放到 MainActor 之外的执行上下文。

SwiftUI vs UIKit 选型

维度SwiftUIUIKit
开发效率声明式,快命令式,样板多
兼容iOS 16+ 体验完整全历史版本
复杂交互快速成长,个别场景需原生控件兜底控件最全最成熟
社区与招聘已是新项目默认存量项目与资深生态
混编UIViewRepresentable / UIViewControllerRepresentable 桥接反向桥接也可

现状:新 App 默认 SwiftUI;遇到 SwiftUI 覆盖不了的交互,用 UIViewRepresentable 包 UIKit 控件,而不是退回整个 UIKit。

并发与主线程纪律

  • UI 必须跑在主线程;耗时工作放后台。现代写法几乎不需要手动 dispatch:
    • Task { ... } 结构化并发;await 点不阻塞线程;
    • 网络/IO 用 async 系统 API,天然离开主线程;
    • 回到 UI 更新:SwiftUI 的视图更新应在 MainActor,网络返回后需回到主上下文再改 UI 状态;
  • 自定义后台任务与跨线程传值:遵守 Sendable 检查,别用全局可变变量绕道;
  • 强引用循环:闭包捕获 self 在 [生命周期长的场景] 会泄漏;对 class 上的回调闭包,用 [weak self] 并判断存在性。

网络与 JSON

swift
struct API {
    static func fetchUsers() async throws -> [User] {
        var req = URLRequest(url: URL(string: "https://api.example.com/users")!)
        req.timeoutInterval = 15
        let (data, response) = try await URLSession.shared.data(for: req)
        guard let http = response as? HTTPURLResponse, http.statusCode == 200 else {
            throw URLError(.badServerResponse)
        }
        return try JSONDecoder().decode([User].self, from: data)
    }
}

工程化要点(完整见网络层与本地存储):统一错误类型、超时与重试、缓存与弱网策略、请求幂等。Swift 里对应工具包括 URLSession 配置、NWPathMonitor 联网检测、ETag/条件请求。

数据持久化

场景方案
轻量偏好设置UserDefaults(小数据;不可存敏感信息)
结构化数据建模SwiftData(iOS 17+)或 Core Data(历史项目)
本地数据库需求SQLite(经 GRDB 等库)
文件/图片缓存应用沙盒目录 + NSCache / 文件管理
敏感凭证Keychain(见网络层与本地存储

从开发到上架

开发 → 归档(Archive)→ TestFlight(内部/外部测试)→ 上架审核 → App Store。签名、证书、描述文件与 CI 流水线的要点在打包、签名与发布,这里只记住一条:签名与描述文件是「设备信任链」,个人调试、真机、TestFlight、上架各需要正确的 Team + 描述文件组合。

常见坑速查

现象对策
主线程卡顿列表滚动掉帧,大图/解码放主线程图片用异步解码/缩略图,IO 与解码都离开主线程
强引用循环页面退出后内存不释放回调/定时器/闭包对 class self 用 [weak self]
退后台丢数据用户输入退后台被杀丢失每次变更即保存,而非退后台补存
强解包崩溃意外 nil 直接 crash一律 if let/guard let/??,保留崩溃证据不如显式处理
Codable 字段不一致后端改字段名解析失败CodingKeys 映射 + 字段兼容(默认值 + decodeIfPresent)
URLSession 缓存失效页面永远旧数据明确缓存策略与 ETag 配合,弱网给「有缓存先展示」
SwiftUI 预览黑屏预览依赖网络/权限预览数据注入 Mock,不要在 preview 里跑真网络
后台任务超时被杀大上传后台做一半BGTaskScheduler 显式申请后台任务并做断点续传
忽略无障碍动态字体下 UI 溢出用系统字体自适应,@ScaledMetric 适配动态字号
版本升级裸奔升 iOS/新 SDK 后 API 废弃报错每年随 Xcode 大版本做一次兼容性升级演练

检查清单

  • [ ] 模型用 struct + Codable,共享可变状态收敛到 actor/观察模型;
  • [ ] 网络调用走 async/await,统一超时、错误类型与重试;
  • [ ] UI 更新在主线程,耗时工作在后台,无裸 ! 强解包;
  • [ ] 用户数据即时保存,敏感信息放 Keychain;
  • [ ] 长列表使用 List/LazyVStack,无一次性巨型视图树;
  • [ ] 已配置真机调试与自动签名,掌握 Archive → TestFlight 流程。

相关链接:技术全景与选型 · 网络层与本地存储 · 打包、签名与发布 · 性能与稳定性 · 写作规范见客户端开发索引

基于 VitePress 构建 · 内容以知识共享方式沉淀