2026年7月28日

Apple 开发生态完整指南

一张从 Swift 语言到 Apple 现代原生框架的开发全景图,帮助初学者建立从入门到独立发布的完整认知。

从零基础到独立发布,本文是一张贯穿 Swift 与 Apple 现代原生框架的实用地图,而非 API 手册。建议按顺序阅读第 1–3 章,之后按需查阅其余章节和附录,具体细节请参考 Apple Developer Documentation。本文于 2026 年 7 月根据 WWDC26 更新:iOS 27 / macOS 27、Xcode 27、Swift 6.4 和 SF Symbols 8 仍处于 beta。学习时请使用当前的 Xcode 26.5 / Swift 6.3 正式工具链,并将这些新能力视为秋季正式版发布前的预览。

第一部分 · 起步

1. 环境与心智准备

1.1 你需要什么

项目要求说明
MacApple Silicon(M 系列)必需Xcode 27 只提供 arm64 版本,无法安装在 Intel Mac 上。macOS 26 Tahoe 是最后一条支持 Intel 的系统线
系统正式版路线:macOS Tahoe 26.2+(配 Xcode 26.5)<br>尝鲜路线:macOS Tahoe 26.4+(配 Xcode 27 beta)SDK and system requirements 页面的实际表格为准,每个小版本都可能变
Xcode正式版从 Mac App Store 获取(当前 26.5,Swift 6.3)<br>Xcode 27 beta 从 开发者下载页获取内含编译器、模拟器、Instruments、预览
开发者账号免费账号即可开始真机调试支持,但有限制:签名证书与描述文件约 7 天需重建,且 App ID 数量、可用设备数、每台设备上的 App 数都有上限(见会员对比)。上架 App Store 需 Apple Developer Program,99 美元/年
设备有真机最好模拟器无法测试相机、传感器、Live Activity 的部分行为、生物识别的真实体验

独立开发者的建议:先用免费账号 + 正式版 Xcode 写 2–3 周,确认自己真的想做这件事,再付 99 美元。不要用 beta 版 Xcode 学习——beta 的 bug 会让你分不清是自己写错了还是工具坏了。等到秋季正式版发布再升级。

1.2 Xcode 的几个关键界面

  • Project Navigator(左侧,⌘1):文件树。
  • Canvas / Preview(右侧):SwiftUI 实时预览,写界面时靠它,⌥⌘P 刷新。
  • Inspector(右侧,⌥⌘0):属性面板。
  • Console(底部,⇧⌘C):print 输出与错误日志。
  • Scheme 选择器(顶部):选择运行目标(哪个 App、哪个设备)。

常用快捷键:⌘R 运行、⌘. 停止、⌘B 编译、⌃⌘←/→ 前后跳转、⇧⌘O 快速打开文件、⌘⇧A 唤起代码操作菜单。

1.3 Xcode 的 AI 辅助

这里有两个容易混淆的东西:

  • 预测式代码补全(predictive code completion):跑在本机的 Apple Silicon 模型上,负责边打字边补全。这一项才和神经网络引擎有关。
  • 编码代理(coding agent):Xcode 27 引入,由你自己选择的模型驱动(可接外部模型),负责多步骤地读写整个项目。它不依赖本机的神经网络引擎。

Xcode 27 还为 SwiftUI 提供了一组官方 agent skills:把 SwiftUI 的约定和当年新 API 的用法直接教给代理,让生成的代码符合当前最佳实践,而不是训练数据里三年前的写法。

对新手的实际意义:你可以让它写,但你必须能读懂它写的东西。这份指南的作用就是让你具备「读懂并判断」的能力。不要跳过基础直接让 AI 生成一切——你会得到一个自己无法维护的项目。

1.4 心智模型:Apple 生态是怎么组织的

把整个生态想成四层:

┌─────────────────────────────────────────────┐
│  你的 App                                    │
├─────────────────────────────────────────────┤
│  界面层    SwiftUI · WidgetKit · RealityKit   │
├─────────────────────────────────────────────┤
│  能力层    SwiftData · StoreKit · HealthKit   │
│           FoundationModels · MapKit · ...     │
├─────────────────────────────────────────────┤
│  基础层    Swift 标准库 · Foundation           │
│           Observation · Swift Concurrency     │
└─────────────────────────────────────────────┘
  • 基础层是语言本身,可跨平台。Swift 与 swift-foundation 在 Linux 上已相当成熟,Android 支持较新、覆盖面窄,都不等于「和 macOS 上完全同一套 API」。
  • 能力层是 Apple 的系统服务包装,绝大多数是「声明一个类型 + 遵守一个协议 + 系统调用你」的模式。
  • 界面层是声明式的:你描述「界面应该长什么样」,框架负责「怎么变成那样」。

一旦你理解「声明式 + 协议驱动 + 值类型优先」这三个词,Apple 的框架设计会变得高度可预测。


2. 学习路线图

下面按阶段划分。每个阶段给出目标、内容、产出。产出很重要——不做东西是学不会的。

阶段 0:一周 · 建立感觉

  • 目标:知道「写代码 → 看到界面」的完整回路。
  • 内容:Xcode 装好;在 Swift Playground(或 Xcode Playground)里写 print、变量、循环;跟着 Apple 官方 SwiftUI Tutorials 走完第一章。
  • 产出:一个显示你名字和一张图片的界面。

阶段 1:三到四周 · Swift 语言

  • 目标:能独立读懂任意一段 Swift 代码。
  • 内容:本文第 3、4 章。重点是可选值值类型 vs 引用类型协议与扩展闭包。泛型和宏可以先建立概念,不必深挖。
  • 产出:一个纯逻辑的命令行程序或 Playground,比如实现一个待办事项的数据模型 + 增删改查。

阶段 2:四到六周 · SwiftUI

  • 目标:能把脑子里的界面画出来,并让它响应数据变化。
  • 内容:本文第 7–10 章。重点是状态管理@State / @Binding / @Observable / @Environment)和布局
  • 产出:一个多页面、有导航、数据在内存中的 App。比如一个不能保存的记账应用。

阶段 3:两到三周 · 数据持久化

  • 目标:App 关掉再打开,数据还在。
  • 内容:第 11、12 章。SwiftData 是主线,@AppStorage 处理轻量设置。
  • 产出:把阶段 2 的 App 加上持久化,再加上 iCloud 同步。

阶段 4:三到四周 · 系统集成

  • 目标:App 不再是一座孤岛。
  • 内容:第 15、17、18 章挑选跟你的 App 相关的:小组件、通知、分享、App Intents/Siri。
  • 产出:给你的 App 加一个锁屏小组件 + 一个 Siri 快捷指令。

阶段 5:两周 · 变现与上架

  • 目标:真的把东西放到 App Store 上。
  • 内容:第 22、23 章。StoreKit 2、TestFlight、App Store Connect。
  • 产出:一个上架的 App,哪怕只有 10 个下载。

阶段 6:持续 · 按需展开

  • 智能功能 → 第 14、16 章(Foundation Models / Core AI)
  • 空间计算 → 第 20 章(RealityKit / visionOS)
  • 图形与游戏 → 第 19 章
  • 多平台扩展 → 第 21 章

关于时间:以上是每天 2 小时左右的估算。全职会快一倍,纯业余可能慢一倍。不要跟别人比进度,比产出。


第二部分 · 语言

3. Swift 语言基础

Swift 的设计目标是「安全、快速、富有表现力」。对新手最友好的一点是:大部分错误在编译时就会被抓住,而不是运行时崩溃。

3.1 变量与常量

swift
let name = "Jerry"        // 常量,不可改
var count = 0             // 变量,可改
count += 1

let pi: Double = 3.14159  // 显式标注类型
let flag = true           // 类型推断为 Bool

习惯:默认写 let,需要改的时候编译器会提醒你改成 var。这不是洁癖,而是让「哪些东西会变」在代码里一目了然。

3.2 基本类型

swift
let i: Int = 42
let d: Double = 3.14
let s: String = "你好"
let b: Bool = true
let arr: [Int] = [1, 2, 3]                    // 数组
let dict: [String: Int] = ["a": 1, "b": 2]    // 字典
let set: Set<Int> = [1, 2, 3]                 // 集合,无序不重复
let tuple: (name: String, age: Int) = ("Jerry", 30)  // 元组

字符串插值:

swift
let age = 30
print("我叫 \(name),今年 \(age) 岁")

多行字符串:

swift
let text = """
    第一行
    第二行
    """

3.3 可选值(Optional)Swift 最重要的概念

「这个值可能不存在」在 Swift 里是类型系统的一部分,写作 T?

swift
var nickname: String? = nil    // 现在没有昵称
nickname = "小刘"               // 现在有了

不能直接使用一个可选值,必须先处理「不存在」的情况。三种方式:

swift
// 1. if let —— 有值就进入分支
if let nickname {
    print("昵称是 \(nickname)")   // 这里 nickname 是 String,不是 String?
} else {
    print("没有昵称")
}

// 2. guard let —— 没值就提前退出(推荐用于函数开头)
func greet(_ nickname: String?) {
    guard let nickname else {
        print("没有昵称,无法问候")
        return
    }
    print("你好,\(nickname)")     // 后续代码里 nickname 都是解包后的
}

// 3. ?? —— 提供默认值
let display = nickname ?? "匿名用户"

可选链:

swift
let length = nickname?.count      // Int?,nickname 为 nil 时整体为 nil

强制解包 `!` 要慎用nickname! 在值为 nil 时会直接崩溃。只在你能百分之百确定有值时使用(例如刚刚检查过),否则用上面三种方式。

3.4 控制流

swift
// if / else
if count > 10 { print("多") } else { print("少") }

// for-in
for i in 1...5 { print(i) }        // 1,2,3,4,5(闭区间)
for i in 1..<5 { print(i) }        // 1,2,3,4(半开区间)
for item in arr { print(item) }
for (key, value) in dict { print("\(key)=\(value)") }

// while
while count < 10 { count += 1 }

// switch —— Swift 的 switch 必须穷尽所有情况
let score = 85
switch score {
case 90...100: print("优秀")
case 60..<90:  print("及格")
default:       print("不及格")
}

switch 支持模式匹配,这是 Swift 很强的地方:

swift
let point = (x: 1, y: 0)
switch point {
case (0, 0):            print("原点")
case (_, 0):            print("在 X 轴上")
case (0, _):            print("在 Y 轴上")
case let (x, y) where x == y: print("在对角线上")
default:                print("其他位置")
}

3.5 函数与闭包

swift
// 基本函数
func add(_ a: Int, _ b: Int) -> Int {
    a + b        // 单表达式函数可省略 return
}

// 参数标签:外部名 + 内部名
func greet(person name: String, from city: String) -> String {
    "你好 \(name),来自 \(city)"
}
greet(person: "Jerry", from: "北京")

// 默认值
func makeCoffee(size: String = "中杯", sugar: Int = 0) { }
makeCoffee()                  // 用默认值
makeCoffee(size: "大杯")

// 可变参数
func sum(_ numbers: Int...) -> Int { numbers.reduce(0, +) }

// 多返回值用元组
func minMax(_ arr: [Int]) -> (min: Int, max: Int)? {
    guard let first = arr.first else { return nil }
    return arr.reduce((first, first)) { (min($0.0, $1), max($0.1, $1)) }
}

闭包是「可以当作值传递的一段代码」。SwiftUI 里到处都是闭包,必须熟练。

swift
// 完整写法
let double: (Int) -> Int = { (x: Int) -> Int in return x * 2 }
// 简化:类型推断 + 省略 return
let double2: (Int) -> Int = { x in x * 2 }
// 再简化:用 $0 表示第一个参数
let double3: (Int) -> Int = { $0 * 2 }

// 尾随闭包:最后一个参数是闭包时,可以写在括号外面
let sorted = [3, 1, 2].sorted { $0 < $1 }
let doubled = [1, 2, 3].map { $0 * 2 }        // [2, 4, 6]
let evens = [1, 2, 3, 4].filter { $0 % 2 == 0 } // [2, 4]
let total = [1, 2, 3].reduce(0, +)             // 6

SwiftUI 里的 Button 就是这个语法:

swift
Button("点我") {          // 这个 {} 是 action 闭包
    print("被点了")
}

3.6 结构体、类、枚举

这是 Swift 的三种自定义类型。先记住一句话:优先用 struct,除非你需要 class 的特性。

swift
// 结构体 —— 值类型,赋值时复制
struct Person {
    var name: String
    var age: Int

    // 计算属性
    var isAdult: Bool { age >= 18 }

    // 方法
    func greeting() -> String { "我是 \(name)" }

    // 修改自身属性的方法必须标 mutating
    mutating func birthday() { age += 1 }
}

var a = Person(name: "Jerry", age: 30)
var b = a          // 复制了一份,b 和 a 互不影响
b.name = "Tom"
print(a.name)      // 仍是 "Jerry"
swift
// 类 —— 引用类型,赋值时共享同一个对象
class Counter {
    var value = 0
    func increment() { value += 1 }

    init(start: Int = 0) { value = start }   // 类必须写 init(除非所有属性有默认值)
    deinit { print("被释放了") }              // 析构
}

let c1 = Counter()
let c2 = c1        // c1 和 c2 指向同一个对象
c2.increment()
print(c1.value)    // 1

什么时候用 class:需要继承、需要引用语义(多处共享同一份可变状态)、需要 deinit、或者框架要求(比如 @Observable 只能用在 class 上)。

swift
// 枚举 —— Swift 的枚举非常强大
enum LoadState {
    case idle
    case loading
    case loaded(items: [String])    // 关联值
    case failed(Error)
}

let state = LoadState.loaded(items: ["a", "b"])
switch state {
case .idle:              print("空闲")
case .loading:           print("加载中")
case .loaded(let items): print("加载了 \(items.count) 项")
case .failed(let error): print("失败:\(error)")
}

// 原始值枚举
enum Direction: String, CaseIterable {
    case north = "北", south = "南", east = "东", west = "西"
}
Direction.allCases.forEach { print($0.rawValue) }

枚举 + 关联值是 Swift 表达「状态机」的标准方式,比一堆 Bool 干净得多。

3.7 属性观察器与惰性属性

swift
struct Settings {
    var volume: Double = 0.5 {
        willSet { print("即将从 \(volume) 变为 \(newValue)") }
        didSet  { print("已从 \(oldValue) 变为 \(volume)") }
    }

    lazy var expensiveThing = computeSomethingSlow()   // 第一次访问时才计算
}

3.8 错误处理

swift
enum NetworkError: Error {
    case notFound
    case unauthorized
    case server(code: Int)
}

func fetch(id: Int) throws -> String {
    guard id > 0 else { throw NetworkError.notFound }
    return "数据 \(id)"
}

// 调用方必须处理
do {
    let data = try fetch(id: 1)
    print(data)
} catch NetworkError.notFound {
    print("没找到")
} catch {
    print("其他错误:\(error)")
}

// try? —— 出错返回 nil
let data = try? fetch(id: -1)      // String?

// try! —— 出错直接崩溃,慎用

让错误对用户友好:

swift
extension NetworkError: LocalizedError {
    var errorDescription: String? {
        switch self {
        case .notFound:      "找不到内容"
        case .unauthorized:  "请先登录"
        case .server(let c): "服务器错误(\(c))"
        }
    }
}

3.9 Swift 6.4 值得知道的新东西

Swift 6.4 随 Xcode 27 一同发布,目前两者都在 beta。当前正式工具链(Xcode 26.5、swift.org 独立工具链)是 Swift 6.3。以下特性你不需要现在就用,但看到时不要困惑:

swift
// 1. anyAppleOS —— 替代五个平台名的简写
@available(anyAppleOS 27, *)
func newFeature() { }

// 以前要写:
// @available(iOS 27, macOS 27, watchOS 27, tvOS 27, visionOS 27, *)

// 条件编译也能用
#if os(anyAppleOS)
    func makeWidget() -> some Widget { ... }
#endif

// 需要排除个别平台时照常叠加
@available(anyAppleOS 27, *)
@available(tvOS, unavailable)
func launch() { }

// 2. defer 中支持异步代码
func process() async throws {
    let handle = try await openResource()
    defer { await handle.close() }    // 无论正常返回还是抛错都会执行
    try await doWork(with: handle)
}

// 3. @diagnose —— 更精细地控制警告

其余改进(Span 等不可复制类型的 for-in 迭代、URL 解析快 4 倍、Swift Testing 与 XCTest 互操作)是底层优化,你会自然受益。


4. Swift 进阶:协议、泛型、宏

4.1 协议(Protocol)

协议定义「一个类型应该具备什么能力」。它是 Apple 框架设计的骨架——你会不断遇到「让你的类型遵守某个协议,然后系统就会调用它」。

swift
protocol Drawable {
    var area: Double { get }        // 要求一个只读属性
    func draw()                     // 要求一个方法
}

struct Circle: Drawable {
    let radius: Double
    var area: Double { .pi * radius * radius }
    func draw() { print("画一个圆") }
}

协议扩展是 Swift 的杀手锏——可以给协议提供默认实现:

swift
extension Drawable {
    func describe() { print("面积为 \(area) 的图形") }
    func draw() { print("默认绘制") }    // 遵守者可以不实现 draw
}

常见的系统协议:

协议作用
Equatable支持 == 比较
Hashable可以放进 Set 或作为字典 key
Comparable支持 <>、排序
Identifiable有唯一 id,SwiftUI 的 List/ForEach 需要
Codable可以与 JSON 等格式互转
Sendable可以安全地跨并发边界传递
CaseIterable枚举可以遍历所有 case

大部分情况下只要声明遵守,编译器就自动合成实现:

swift
struct Todo: Identifiable, Codable, Hashable {
    let id = UUID()
    var title: String
    var done = false
}
// 就这样,JSON 编解码、去重、SwiftUI 列表全部可用

4.2 扩展(Extension)

扩展可以给任何类型(包括系统类型)添加功能,不需要继承:

swift
extension String {
    var isValidEmail: Bool {
        contains("@") && contains(".")
    }
}
"[email protected]".isValidEmail    // true

extension Double {
    var asCurrency: String {
        formatted(.currency(code: "CNY"))
    }
}

扩展也是组织代码的好工具——把一个大类型按职责拆成多个扩展。

4.3 泛型(Generics)

泛型让你写「对任意类型都成立」的代码:

swift
func firstElement<T>(of array: [T]) -> T? {
    array.first
}

// 带约束的泛型
func maxElement<T: Comparable>(of array: [T]) -> T? {
    array.max()
}

// 泛型类型
struct Stack<Element> {
    private var items: [Element] = []
    mutating func push(_ item: Element) { items.append(item) }
    mutating func pop() -> Element? { items.popLast() }
    var isEmpty: Bool { items.isEmpty }
}

var s = Stack<Int>()
s.push(1); s.push(2)

`some` 与 `any`

swift
func makeShape() -> some Drawable { Circle(radius: 1) }
//                  ↑ 「某个确定的、但我不告诉你具体是什么的」类型 —— 编译期确定,高性能

func processAny(_ shape: any Drawable) { shape.draw() }
//                       ↑ 「任意遵守协议的」类型 —— 运行期动态派发,可以放进异构数组

SwiftUI 里 var body: some View 就是前者。你不需要写出那个复杂到爆炸的嵌套类型,编译器帮你记住。

4.4 宏(Macro)

Swift 宏在编译期生成代码。你主要是使用者而不是编写者。你会用到的宏:

来自作用
@ObservableObservation让类的属性变化能被 SwiftUI 观察
@ModelSwiftData让类成为可持久化模型
@GenerableFoundationModels让类型可以被 LLM 结构化生成
@Test / #expectSwift Testing定义测试
#PreviewSwiftUI定义预览
#PredicateFoundation类型安全的查询谓词

想看宏展开成了什么:右键宏名称 → Expand Macro。这是理解框架行为的好办法。

4.5 内存管理与引用循环

Swift 用 ARC(自动引用计数)管理 class 实例。99% 的情况你不用管,但有一个坑要知道:两个对象互相强引用会导致内存泄漏

swift
class Parent { var child: Child? }
class Child { weak var parent: Parent? }    // weak 打破循环

在闭包里捕获 self 时同理:

swift
class ViewModel {
    var onUpdate: (() -> Void)?
    func setup() {
        onUpdate = { [weak self] in         // 用 [weak self] 避免循环
            guard let self else { return }
            self.doSomething()
        }
    }
    func doSomething() {}
}

用 struct 就基本不用担心这个问题——这也是「优先用 struct」的理由之一。


5. Swift 并发

现代 Swift 的并发模型是 async/await + Actor。忘掉回调地狱和 `DispatchQueue`——那是老代码里的东西。

5.1 async / await

swift
func loadUser(id: Int) async throws -> User {
    let url = URL(string: "https://api.example.com/user/\(id)")!
    let (data, _) = try await URLSession.shared.data(from: url)
    return try JSONDecoder().decode(User.self, from: data)
}

// 调用
Task {
    do {
        let user = try await loadUser(id: 1)
        print(user.name)
    } catch {
        print("失败:\(error)")
    }
}

await 的含义是「这里可能会暂停,让出线程给别人用,完成后再继续」。代码读起来是同步的,执行是异步的。

5.2 并行执行

swift
// 串行:总耗时 = A + B
let a = try await loadUser(id: 1)
let b = try await loadUser(id: 2)

// 并行:总耗时 = max(A, B)
async let userA = loadUser(id: 1)
async let userB = loadUser(id: 2)
let (a2, b2) = try await (userA, userB)

// 数量不定时用 TaskGroup
func loadAll(ids: [Int]) async throws -> [User] {
    try await withThrowingTaskGroup(of: User.self) { group in
        for id in ids {
            group.addTask { try await loadUser(id: id) }
        }
        var results: [User] = []
        for try await user in group { results.append(user) }
        return results
    }
}

5.3 Task 与取消

swift
let task = Task {
    for i in 0..<1000 {
        try Task.checkCancellation()    // 检查是否被取消
        await process(i)
    }
}
task.cancel()

在 SwiftUI 里,.task {} 修饰符会在视图消失时自动取消:

swift
struct UserView: View {
    @State private var user: User?

    var body: some View {
        Text(user?.name ?? "加载中")
            .task {                       // 视图出现时开始,消失时自动取消
                user = try? await loadUser(id: 1)
            }
    }
}

5.4 Actor 与数据隔离

Actor 是「保证内部状态不会被并发访问搞乱」的类型:

swift
actor ImageCache {
    private var cache: [URL: Data] = [:]

    func image(for url: URL) -> Data? { cache[url] }
    func store(_ data: Data, for url: URL) { cache[url] = data }
}

let cache = ImageCache()
await cache.store(data, for: url)     // 访问 actor 需要 await

`@MainActor` 是最常用的 actor——它代表主线程。所有 UI 更新必须在主线程:

swift
@MainActor
@Observable
final class TodoStore {
    var todos: [Todo] = []

    func refresh() async {
        let fetched = await fetchFromServer()   // 这一步在后台
        todos = fetched                          // 回到主线程更新
    }
}

SwiftUI 的 View 默认就是 @MainActor,所以在 body.task 里更新 @State 是安全的。

5.5 Sendable 与严格并发

严格并发检查由语言模式决定,不是装了哪个编译器决定的。Xcode 里可以在 Swift 6 / Swift 5 / Swift 4.2 / Swift 4 语言模式之间选择,老项目可以继续停在 Swift 5 模式下渐进迁移。

Swift 6 语言模式下,严格并发检查默认开启:跨越并发边界的数据必须是 `Sendable` 的(即可以安全地在多个任务间传递)。新项目模板默认就是 Swift 6 模式,建议保持。

  • 所有属性都是值类型或不可变的 struct / enum → 自动 Sendable
  • class 需要手动保证(用 final + 不可变属性,或标 @unchecked Sendable 并自己加锁)
  • actor 天然 Sendable
swift
struct Todo: Sendable { ... }              // 自动推断,通常不用写

final class Config: Sendable {             // 需要所有属性都是 let 且 Sendable
    let apiKey: String
    init(apiKey: String) { self.apiKey = apiKey }
}

新手建议:遇到「Sending value of non-Sendable type...」这类编译错误时,先问自己「这个数据真的需要跨线程吗?」。往往正确答案是给类型加 @MainActor,而不是加 @unchecked Sendable 硬压过去。

5.6 AsyncSequence

「随时间陆续到达的一串值」:

swift
// 逐行读文件
for try await line in url.lines {
    print(line)
}

// 自定义流
let stream = AsyncStream<Int> { continuation in
    Task {
        for i in 0..<10 {
            continuation.yield(i)
            try? await Task.sleep(for: .seconds(1))
        }
        continuation.finish()
    }
}
for await value in stream { print(value) }

StoreKit 的 Transaction.updates、Foundation Models 的流式输出都是 AsyncSequence


6. Swift Package Manager

SPM 是 Apple 官方的依赖管理工具,深度集成在 Xcode 里,新项目的默认选择。CocoaPods、Carthage 是第三方工具,仍有存量项目在用,但没有理由为新项目引入。

6.1 添加依赖

Xcode 里:File → Add Package Dependencies…,粘贴 GitHub 地址即可。

6.2 创建自己的包

把可复用的代码抽成本地包,是保持大项目整洁的最佳方式:

下面按当前正式工具链(Xcode 26.5 / Swift 6.3)写。swift-tools-versionplatforms 里可用的版本取决于你装的工具链——用 Xcode 27 时才能写 .v27

swift
// Package.swift
// swift-tools-version: 6.2
import PackageDescription

let package = Package(
    name: "MyKit",
    platforms: [.iOS(.v26), .macOS(.v26)],   // 用 Xcode 27 工具链时可写 .v27
    products: [
        .library(name: "MyKit", targets: ["MyKit"])
    ],
    dependencies: [],
    targets: [
        .target(name: "MyKit"),
        .testTarget(name: "MyKitTests", dependencies: ["MyKit"])
    ]
)

命令行:

bash
swift package init --type library
swift build
swift test
swift run

6.3 值得知道的包

  • Swift Package Index —— 已加入 Apple,是查找 Swift 包的权威入口
  • swift-collections —— DequeOrderedSet 等标准库没有的容器
  • swift-algorithms —— chunkedwindows 等序列算法
  • swift-async-algorithms —— AsyncSequence 的组合操作
  • foundation-models-utilities —— WWDC26 新推出,LLM 工作流工具

独立开发者的建议:Apple 生态里第三方依赖越少越好。系统框架覆盖度极高,每引入一个包都是未来某次系统升级时的潜在维护成本。


第三部分 · 界面

7. SwiftUI 核心

SwiftUI 是 Apple 全平台统一的声明式界面框架。一套代码,六个平台(iOS / iPadOS / macOS / watchOS / tvOS / visionOS)。这是新手唯一应该学的界面框架。

7.1 声明式思维

命令式(UIKit 的老方式):「创建一个标签,设置文字,加到父视图上,当数据变了就找到这个标签把文字改掉」。

声明式(SwiftUI):「界面上有一个标签,它的文字永远等于 count 的值」。数据变了,界面自动跟着变,你不需要写更新代码。

swift
struct CounterView: View {
    @State private var count = 0

    var body: some View {
        VStack(spacing: 16) {
            Text("计数:\(count)")        // 永远等于 count
                .font(.largeTitle)
            Button("加一") { count += 1 }  // 只改数据,界面自己更新
        }
    }
}

7.2 View 协议

一切界面元素都遵守 View 协议,只要求一个 body

swift
struct MyView: View {
    var body: some View {
        Text("Hello")
    }
}

关键理解body 不是「界面」,而是「描述界面的一份配方」。SwiftUI 会在数据变化时反复调用 body,比较新旧配方的差异,只更新真正变了的部分。所以:

  • body 里不要做耗时操作
  • body 里不要有副作用(不要在里面发网络请求、写数据库)
  • struct 每次重建的开销极小,不用担心性能

7.3 常用视图组件

swift
// 文本
Text("标题")
    .font(.title)
    .fontWeight(.semibold)
    .foregroundStyle(.secondary)

// 图片
Image(systemName: "star.fill")        // SF Symbols,系统内置 7000+ 图标
    .symbolRenderingMode(.multicolor)
Image("myPhoto")                       // Assets 里的图片
    .resizable()
    .scaledToFit()

// 网络图片(iOS 27 起默认支持 HTTP 缓存)
AsyncImage(url: url) { image in
    image.resizable().scaledToFill()
} placeholder: {
    ProgressView()
}

// 按钮
Button("确定") { }
Button("删除", systemImage: "trash", role: .destructive) { }

// 输入
@State var text = ""
TextField("请输入", text: $text)
SecureField("密码", text: $password)
TextEditor(text: $longText)

// 选择
Toggle("开启通知", isOn: $notificationsEnabled)
Slider(value: $volume, in: 0...1)
Stepper("数量:\(count)", value: $count, in: 1...10)
Picker("尺寸", selection: $size) {
    Text("小").tag(Size.small)
    Text("中").tag(Size.medium)
}
DatePicker("日期", selection: $date, displayedComponents: .date)
ColorPicker("颜色", selection: $color)

// 状态指示
ProgressView()                          // 转圈
ProgressView(value: 0.7)                // 进度条
Label("收藏", systemImage: "heart")
Link("访问网站", destination: url)
ShareLink(item: url)                    // 系统分享

7.4 修饰符(Modifier)

修饰符返回一个新的视图,包裹原视图。所以顺序很重要

swift
Text("Hi")
    .padding()              // 先加内边距
    .background(.blue)      // 再上背景 → 背景包含内边距

Text("Hi")
    .background(.blue)      // 先上背景 → 背景只有文字大小
    .padding()              // 再加内边距 → 内边距在背景外面

常用修饰符分类:

swift
// 尺寸与位置
.frame(width: 100, height: 50)
.frame(maxWidth: .infinity)             // 尽量占满
.padding(.horizontal, 16)
.offset(x: 10, y: 0)

// 外观
.background(.regularMaterial)           // 材质背景(毛玻璃)
.foregroundStyle(.blue)
.clipShape(.rect(cornerRadius: 12))
.overlay { RoundedRectangle(cornerRadius: 12).stroke(.gray) }
.shadow(radius: 4)
.opacity(0.8)

// 交互
.onTapGesture { }
.disabled(isLoading)
.contextMenu { Button("删除") { } }
.swipeActions { Button("归档") { } }

// 生命周期
.task { await load() }                  // 出现时执行异步任务
.onAppear { }
.onChange(of: query) { old, new in }

// 弹出
.sheet(isPresented: $showSheet) { DetailView() }
.sheet(item: $selectedItem) { item in DetailView(item: item) }
.fullScreenCover(isPresented: $show) { }
.alert("确认删除?", isPresented: $showAlert) {
    Button("删除", role: .destructive) { }
    Button("取消", role: .cancel) { }
}
.confirmationDialog("选择操作", isPresented: $showDialog) { }
.popover(isPresented: $showPopover) { }
iOS 27 新增
alertconfirmationDialog 现在也支持 sheet 那样的 item: 绑定写法,绑定值一被设置就自动弹出,不必再单独维护一个 Bool

7.5 容器与列表

swift
// 栈
VStack(alignment: .leading, spacing: 8) { }    // 垂直
HStack { }                                      // 水平
ZStack(alignment: .topTrailing) { }             // 叠放

// 滚动
ScrollView { LazyVStack { } }
ScrollView(.horizontal) { LazyHStack { } }

// 网格
LazyVGrid(columns: [GridItem(.adaptive(minimum: 100))]) { }

// 列表 —— 最常用
List(todos) { todo in
    Text(todo.title)
}

// 带分区、删除、移动的列表
List {
    Section("今天") {
        ForEach(todos) { todo in
            TodoRow(todo: todo)
        }
        .onDelete { indexSet in delete(at: indexSet) }
        .onMove { from, to in move(from: from, to: to) }
    }
}

// 表格(macOS / iPadOS)
Table(people) {
    TableColumn("姓名", value: \.name)
    TableColumn("年龄") { Text("\($0.age)") }
}
iOS 27 新增:通用重排序。以前只有 List 支持拖拽排序,现在 LazyVGridLazyHStack 等任意容器都能用同一套 API 重排,watchOS 也首次支持。swipeActionsContainer 可以让整个 ScrollView 支持侧滑操作。

7.6 预览

swift
#Preview {
    CounterView()
}

#Preview("深色模式") {
    CounterView()
        .preferredColorScheme(.dark)
}

#Preview("带数据", traits: .sizeThatFitsLayout) {
    TodoRow(todo: .init(title: "买牛奶"))
}

预览是 SwiftUI 开发效率的核心。养成给每个视图写预览的习惯,你的迭代速度会快好几倍。


8. SwiftUI 状态管理

这是新手最容易困惑的部分。记住一条主线:数据只有一个真相来源(Single Source of Truth),其他地方都是它的引用。

8.1 五个属性包装器

包装器用于谁拥有数据
@State视图私有的、简单的状态当前视图
@Binding从父视图传来的可读写引用父视图
@Environment从环境读取共享数据祖先视图或系统
@Bindable@Observable 对象取绑定别处
@AppStorageUserDefaults 同步的设置项系统偏好设置

8.2 @State 与 @Binding

swift
struct ParentView: View {
    @State private var isOn = false          // 真相来源在这里

    var body: some View {
        VStack {
            Text(isOn ? "开" : "关")
            ChildToggle(isOn: $isOn)          // $ 取出 Binding
        }
    }
}

struct ChildToggle: View {
    @Binding var isOn: Bool                   // 引用父视图的状态

    var body: some View {
        Toggle("开关", isOn: $isOn)
    }
}

@State 一律加 private——它是视图的内部实现细节。

Xcode 27 变化
State 改成了一个宏,存进 @State 的 class 实例会惰性初始化,且在整个视图生命周期内只创建一次。以前需要小心 @State 初始值被反复构造的问题,现在自动解决了。 注意这是工具链行为而非设备端行为:只要用 Xcode 27 编译,该行为会回溯适用到 @Observable 引入的那批系统版本(iOS 17 / macOS 14 一线),不是只在 iOS 27 设备上才生效。

8.3 @Observable现代的数据模型

这是 Observation 框架提供的宏,取代了老的 `ObservableObject` / `@Published` / `@StateObject` / `@ObservedObject`

swift
import Observation

@MainActor
@Observable
final class TodoStore {
    var todos: [Todo] = []
    var isLoading = false

    // 不想被观察的属性
    @ObservationIgnored private var cache: [String: Data] = [:]

    func add(_ title: String) {
        todos.append(Todo(title: title))
    }

    func load() async {
        isLoading = true
        defer { isLoading = false }
        todos = await fetchTodos()
    }
}

使用:

swift
struct TodoListView: View {
    @State private var store = TodoStore()      // 用 @State 持有,不是 @StateObject

    var body: some View {
        List(store.todos) { todo in
            Text(todo.title)
        }
        .task { await store.load() }
    }
}

`@Observable` 的优势:只有真正被 body 读取的属性变化时才重绘。老的 ObservableObject 是「任何 @Published 变化都重绘整个视图」,性能差很多。

需要对 @Observable 对象的属性做双向绑定时用 @Bindable

swift
struct EditView: View {
    @Bindable var todo: Todo        // Todo 是 @Observable class

    var body: some View {
        TextField("标题", text: $todo.title)
    }
}

8.4 @Environment

用于跨多层视图传递数据,避免逐层透传:

swift
// 1. 定义(iOS 17+ 的宏写法)
extension EnvironmentValues {
    @Entry var todoStore = TodoStore()
}

// 2. 注入
@main
struct MyApp: App {
    @State private var store = TodoStore()

    var body: some Scene {
        WindowGroup {
            ContentView()
                .environment(\.todoStore, store)
        }
    }
}

// 3. 读取
struct DeepChildView: View {
    @Environment(\.todoStore) private var store
    var body: some View { Text("\(store.todos.count)") }
}

@Observable 类型有更简洁的写法:

swift
ContentView().environment(store)          // 注入
@Environment(TodoStore.self) private var store   // 读取

系统也提供了大量环境值:

swift
@Environment(\.colorScheme) private var colorScheme          // 深浅色
@Environment(\.dismiss) private var dismiss                  // 关闭当前页面
@Environment(\.openURL) private var openURL
@Environment(\.horizontalSizeClass) private var sizeClass    // 紧凑/常规布局
@Environment(\.dynamicTypeSize) private var typeSize         // 用户字号设置
@Environment(\.scenePhase) private var scenePhase            // 前台/后台
@Environment(\.locale) private var locale

8.5 @AppStorage

轻量设置项的最简单方案:

swift
struct SettingsView: View {
    @AppStorage("username") private var username = ""
    @AppStorage("isDarkMode") private var isDarkMode = false
    @AppStorage("fontSize") private var fontSize = 16.0

    var body: some View {
        Form {
            TextField("用户名", text: $username)
            Toggle("深色模式", isOn: $isDarkMode)
        }
    }
}

数据自动存进 UserDefaults,App 重启后还在。只适合小数据(设置项、开关、上次选择的 tab),不要用它存业务数据。

8.6 状态管理决策树

数据只在一个视图里用,且很简单?
  → @State

需要传给子视图并让子视图能改?
  → @Binding

有业务逻辑、需要跨视图共享?
  → @Observable class + @State 持有 + @Environment 分发

只是一个用户偏好设置?
  → @AppStorage

需要持久化的业务数据?
  → SwiftData(见第 11 章)

不要过度设计。很多教程会教你 MVVM、Clean Architecture、Redux 之类的。作为独立开发者,先用最简单的方式做出来。当某个视图的 body 超过 100 行、或者状态逻辑开始重复时,再抽出 @Observable 模型也不迟。


9. 导航、布局与动画

9.1 导航

swift
// NavigationStack —— 单栏推入式导航(iPhone 主力)
NavigationStack {
    List(todos) { todo in
        NavigationLink(todo.title, value: todo)
    }
    .navigationTitle("待办")
    .navigationDestination(for: Todo.self) { todo in
        TodoDetailView(todo: todo)
    }
}

// 可编程导航:用路径数组控制
@State private var path: [Todo] = []

NavigationStack(path: $path) {
    ...
}
// 之后可以 path.append(todo) 或 path.removeAll() 直接跳转/返回

// NavigationSplitView —— 双栏/三栏(iPad、Mac)
NavigationSplitView {
    SidebarView()          // 侧边栏
} content: {
    ListView()             // 中间列(可选)
} detail: {
    DetailView()           // 详情
}

// TabView —— 底部标签
TabView {
    Tab("首页", systemImage: "house") { HomeView() }
    Tab("设置", systemImage: "gear") { SettingsView() }
    Tab(role: .search) { SearchView() }     // 搜索标签有特殊样式
}

跨平台建议:用 NavigationSplitView 写一次,它在 iPhone 上会自动退化成推入式导航,在 iPad / Mac 上展开为多栏。

9.2 工具栏

swift
.toolbar {
    ToolbarItem(placement: .topBarLeading) {
        Button("取消") { dismiss() }
    }
    ToolbarItemGroup(placement: .primaryAction) {
        Button("保存") { save() }
        Menu("更多") { ... }
    }
}
iOS 27 新增的工具栏控制(下面写的是这组能力的形态,具体符号名以 Toolbars 文档为准,它们分属修饰符、容器和 placement 三类): - visibility priority —— 窗口变窄时声明优先保留哪些工具栏组 - overflow menu —— 把低优先级项永久收进溢出菜单(容器形式,如 ToolbarOverflowMenu { … }) - `.topBarPinnedTrailing` placement —— 把关键操作(如分享)永远钉在尾端,用在 ToolbarItem(placement:) 上 - `.toolbarMinimizeBehavior(_:for:)` —— 滚动时自动收起导航栏 这组 API 让同一份代码在 iPhone、iPad、Mac 上都能有合理的工具栏布局,值得一开始就用对。

9.3 搜索

swift
@State private var query = ""

List(filteredItems) { ... }
    .searchable(text: $query, prompt: "搜索待办")
    .searchSuggestions {
        ForEach(suggestions) { Text($0).searchCompletion($0) }
    }
    .searchScopes($scope) {
        Text("全部").tag(Scope.all)
        Text("未完成").tag(Scope.pending)
    }

9.4 布局系统

SwiftUI 的布局是「父视图提议尺寸 → 子视图决定自己的尺寸 → 父视图放置子视图」的三步协商。理解这一点能解决 90% 的布局困惑。

swift
// Spacer 推开空间
HStack {
    Text("左")
    Spacer()
    Text("右")
}

// 布局优先级
HStack {
    Text("这段很长很长的文字").layoutPriority(1)
    Text("短的")
}

// 固定尺寸(不被压缩)
Text("不换行").fixedSize()

// 自定义布局:ViewThatFits 自动选第一个装得下的方案
ViewThatFits {
    HStack { A(); B(); C() }      // 宽的时候用这个
    VStack { A(); B(); C() }      // 窄的时候用这个
}

// GeometryReader 读取可用空间(能不用就不用,会破坏布局协商)
GeometryReader { proxy in
    Text("宽度:\(proxy.size.width)")
}

// Grid —— 需要对齐的表格布局
Grid {
    GridRow { Text("姓名"); Text("Jerry") }
    GridRow { Text("城市"); Text("北京") }
}

9.5 动画

swift
// 隐式动画:状态变了就自动过渡
@State private var isExpanded = false

Rectangle()
    .frame(height: isExpanded ? 200 : 100)
    .animation(.spring, value: isExpanded)     // 只对 isExpanded 的变化做动画

// 显式动画
Button("展开") {
    withAnimation(.spring(duration: 0.4)) {
        isExpanded.toggle()
    }
}

// 转场:视图出现/消失的方式
if showDetail {
    DetailView()
        .transition(.move(edge: .bottom).combined(with: .opacity))
}

// 匹配几何:两个视图之间的形变动画
@Namespace private var namespace

// 视图 A
Image(...).matchedGeometryEffect(id: "hero", in: namespace)
// 视图 B
Image(...).matchedGeometryEffect(id: "hero", in: namespace)

// 导航转场(iOS 18+):从卡片放大到详情页
NavigationLink { DetailView() } label: { CardView() }
    .matchedTransitionSource(id: item.id, in: namespace)

// 阶段动画:多步骤动画
Image(systemName: "bell")
    .phaseAnimator([0, -20, 20, 0]) { view, angle in
        view.rotationEffect(.degrees(angle))
    }

// 关键帧动画:多属性独立时间线
.keyframeAnimator(initialValue: AnimationValues()) { view, value in
    view.scaleEffect(value.scale).offset(y: value.offset)
} keyframes: { _ in
    KeyframeTrack(\.scale) {
        SpringKeyframe(1.2, duration: 0.2)
        SpringKeyframe(1.0, duration: 0.3)
    }
}

// SF Symbols 动效
Image(systemName: "heart.fill")
    .symbolEffect(.bounce, value: likeCount)
    .contentTransition(.symbolEffect(.replace))

动画的原则:动画服务于「解释状态变化」,不是装饰。默认用 .spring,它在 Apple 平台上感觉最自然。

9.6 手势

swift
.onTapGesture(count: 2) { }
.onLongPressGesture { }

@State private var offset = CGSize.zero
.gesture(
    DragGesture()
        .onChanged { offset = $0.translation }
        .onEnded { _ in withAnimation { offset = .zero } }
)

@State private var scale = 1.0
.gesture(MagnifyGesture().onChanged { scale = $0.magnification })

// 组合手势
.gesture(dragGesture.simultaneously(with: magnifyGesture))

10. 设计语言与界面周边框架

10.1 Liquid Glass 设计语言

iOS 26 引入的 Liquid Glass 是当前 Apple 全平台统一的视觉语言:半透明、有折射感的材质层,内容在下面流动。iOS 27 在此基础上刷新了材质、精修了字体排印,并统一了标签栏与导航栏的外观。

作为 SwiftUI 开发者,你基本上什么都不用做——用标准组件(NavigationStackTabView.toolbarButton)就自动获得正确外观。需要手动控制时:

swift
// 给自定义视图套玻璃效果
MyCustomBar()
    .glassEffect(in: .rect(cornerRadius: 20))

// 材质背景
.background(.regularMaterial)      // 还有 .thin / .thick / .ultraThin / .ultraThick

// 按钮样式
Button("确定") { }.buttonStyle(.glass)
Button("确定") { }.buttonStyle(.borderedProminent)

关键原则:不要跟系统对着干。你自己画一个完全自定义的导航栏,在下一次系统更新时就会显得格格不入。Apple 生态的美学收益来自「顺从系统 + 在内容区做出你的品牌」。

10.2 SF Symbols

系统内置矢量图标库,自动适配字重、字号、深浅色,还支持动效。SF Symbols 8(beta)收录 7000+ 符号。

下载 SF Symbols App 来浏览和搜索。

swift
Image(systemName: "heart.fill")
    .font(.title)
    .symbolRenderingMode(.hierarchical)      // 层次渲染
    .foregroundStyle(.red, .pink)            // 多色
    .symbolVariant(.circle)                  // 变体
    .symbolEffect(.pulse)                    // 动效

10.3 Icon Composer

Apple 官方的 App 图标制作工具,一份设计自动生成各平台、各模式(浅色/深色/着色/透明玻璃)的图标。在 Apple Developer 下载页获取。对独立开发者是很大的省力工具。

10.4 Swift Charts

声明式的图表框架,语法和 SwiftUI 完全一致。

swift
import Charts

struct SalesChart: View {
    let data: [Sale]

    var body: some View {
        Chart(data) { sale in
            BarMark(
                x: .value("月份", sale.month),
                y: .value("销售额", sale.amount)
            )
            .foregroundStyle(by: .value("产品", sale.product))
        }
        .chartXAxis { AxisMarks(values: .automatic) }
        .chartLegend(position: .bottom)
        .frame(height: 200)
    }
}

支持 BarMark / LineMark / AreaMark / PointMark / SectorMark(饼图)/ RuleMark 等,可叠加组合。做数据类 App 时它能省掉引入第三方图表库。

10.5 TipKit

系统统一的功能引导提示框架。比自己写引导层好——它自带出现频率控制、用户已读状态、跨设备同步。

swift
import TipKit

struct FavoriteTip: Tip {
    var title: Text { Text("收藏这篇文章") }
    var message: Text? { Text("点击星标,稍后在收藏夹里查看") }
    var image: Image? { Image(systemName: "star") }
}

// 在 App 启动时配置
try? Tips.configure()

// 在界面上展示
Button("收藏", systemImage: "star") { }
    .popoverTip(FavoriteTip())

10.6 与 UIKit / AppKit 互操作

虽然新项目不该用 UIKit,但偶尔需要接入某个 SwiftUI 还没覆盖的能力:

swift
struct WebView: UIViewRepresentable {
    let url: URL

    func makeUIView(context: Context) -> WKWebView { WKWebView() }
    func updateUIView(_ view: WKWebView, context: Context) {
        view.load(URLRequest(url: url))
    }
}

macOS 用 NSViewRepresentable,反向嵌入用 UIHostingController / NSHostingController

什么时候真的需要WKWebViewAVPlayerViewController 的某些高级配置、某些老的第三方 SDK。除此之外,先在文档里找 SwiftUI 的对应方案。

10.7 可访问性

不是可选项。SwiftUI 默认已经做了大部分工作,你只需要在自定义元素上补充:

swift
Image(systemName: "star.fill")
    .accessibilityLabel("已收藏")
    .accessibilityHint("双击取消收藏")
    .accessibilityAddTraits(.isButton)

// 把一组元素合并成一个可访问元素
HStack { Image(...); Text("标题"); Text("副标题") }
    .accessibilityElement(children: .combine)

其他要点:不要只用颜色传达信息、支持动态字号(用 .font(.body) 而不是 .font(.system(size: 16)))、尊重「减弱动态效果」设置。

10.8 本地化

Xcode 15 起用 String Catalog.xcstrings)。

  1. 项目里新建 Localizable.xcstrings
  2. 代码里正常写 Text("你好"),Xcode 自动提取
  3. 在 Catalog 编辑器里添加目标语言并翻译
swift
Text("你好")                              // 自动本地化
Text("共 \(count) 项")                     // 支持复数变体,在 Catalog 里配置
String(localized: "确认删除", comment: "删除确认弹窗的标题")

// 格式化(自动跟随用户地区)
Text(date, format: .dateTime.year().month().day())
Text(price, format: .currency(code: "CNY"))
Text(bytes, format: .byteCount(style: .file))

第四部分 · 数据

11. SwiftData

SwiftData 是 Apple 的现代持久化框架,用宏和 Swift 类型系统包装了 Core Data 的能力。新项目不要用 Core Data

11.1 定义模型

swift
import SwiftData

@Model
final class Todo {
    var title: String
    var isDone: Bool
    var createdAt: Date
    var priority: Priority

    // 关系
    @Relationship(deleteRule: .cascade)
    var subtasks: [Subtask] = []

    // 不参与持久化
    @Transient var isEditing = false

    // 唯一约束
    #Unique<Todo>([\.title])

    // 建索引,加速查询
    #Index<Todo>([\.createdAt])

    init(title: String, priority: Priority = .normal) {
        self.title = title
        self.isDone = false
        self.createdAt = .now
        self.priority = priority
    }
}

enum Priority: Int, Codable, CaseIterable {
    case low, normal, high
}

@Model 宏做的事:把类变成可持久化的实体、自动实现 Observable(所以 SwiftUI 能观察它的变化)、生成存储属性的读写逻辑。

iOS 27 新增
@Attribute(.codable) 让任何遵守 Codable 的自定义类型或第三方类型直接作为属性存储,不必再手动拆成基础字段。

11.2 配置容器

swift
@main
struct MyApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
        .modelContainer(for: Todo.self)      // 一行搞定
    }
}

需要更多控制时:

swift
let container = try ModelContainer(
    for: Todo.self, Subtask.self,
    configurations: ModelConfiguration(
        isStoredInMemoryOnly: false,
        cloudKitDatabase: .automatic      // 开启 iCloud 同步
    )
)

11.3 查询

swift
struct TodoListView: View {
    // 最简单:查全部
    @Query private var todos: [Todo]

    // 排序
    @Query(sort: \Todo.createdAt, order: .reverse)
    private var recentTodos: [Todo]

    // 过滤 + 排序 + 限量
    @Query(
        filter: #Predicate<Todo> { !$0.isDone },
        sort: [SortDescriptor(\.priority, order: .reverse),
               SortDescriptor(\.createdAt)]
    )
    private var pendingTodos: [Todo]

    var body: some View {
        List(pendingTodos) { todo in
            Text(todo.title)
        }
    }
}

#Predicate 是类型安全的:写错属性名编译不过,而不是运行时才报错。

iOS 27 新增
分区查询。给 @QuerysectionBy: 参数(一个从模型根出发、指向字符串属性的 KeyPath),直接得到分好组的结果,配合 Section 使用,不用自己在内存里 Dictionary(grouping:)。 社区还提到了枚举谓词、组合谓词等改进,但我没在 Apple 官方的 What's new in SwiftData 材料里核到明确的「新增」表述——用之前请以文档为准。

动态查询(过滤条件由用户输入决定)需要在 init 里构造:

swift
struct SearchableTodoList: View {
    @Query private var todos: [Todo]

    init(searchText: String) {
        _todos = Query(filter: #Predicate<Todo> {
            searchText.isEmpty || $0.title.localizedStandardContains(searchText)
        })
    }

    var body: some View { List(todos) { Text($0.title) } }
}

11.4 增删改

swift
struct AddTodoView: View {
    @Environment(\.modelContext) private var context
    @State private var title = ""

    var body: some View {
        Form {
            TextField("标题", text: $title)
            Button("添加") {
                context.insert(Todo(title: title))   // 插入
                // 不需要手动 save(),SwiftUI 环境下会自动保存
            }
        }
    }
}

// 删除
context.delete(todo)

// 修改:直接改属性即可
todo.isDone = true

// 手动保存(在非 SwiftUI 上下文中需要)
try context.save()

// 批量删除
try context.delete(model: Todo.self, where: #Predicate { $0.isDone })

11.5 在 SwiftUI 之外使用

后台任务、命令行工具、测试里需要手动创建 context:

swift
@ModelActor
actor TodoImporter {
    func importTodos(from data: Data) throws {
        let items = try JSONDecoder().decode([TodoDTO].self, from: data)
        for item in items {
            modelContext.insert(Todo(title: item.title))
        }
        try modelContext.save()
    }
}

// 使用
let importer = TodoImporter(modelContainer: container)
try await importer.importTodos(from: data)

@ModelActor 宏会生成一个绑定到独立 ModelContext 的 actor,保证后台写入不会跟主线程冲突。

iOS 27 新增
ResultsObserverHistoryObserver 让你在任何地方(不限于 SwiftUI 视图)观察数据变化,用于驱动状态对象或响应特定模型的更新。这补上了 SwiftData 之前最明显的缺口。

11.6 数据迁移

模型改结构时需要迁移。轻量改动(加可选属性、加新模型)SwiftData 自动处理。复杂改动需要写 Schema:

swift
enum TodoSchemaV1: VersionedSchema {
    static var versionIdentifier = Schema.Version(1, 0, 0)
    static var models: [any PersistentModel.Type] { [Todo.self] }
}

enum TodoSchemaV2: VersionedSchema { ... }

enum TodoMigrationPlan: SchemaMigrationPlan {
    static var schemas: [any VersionedSchema.Type] {
        [TodoSchemaV1.self, TodoSchemaV2.self]
    }
    static var stages: [MigrationStage] {
        [.lightweight(fromVersion: TodoSchemaV1.self, toVersion: TodoSchemaV2.self)]
    }
}

给独立开发者的忠告:上架前把模型结构想清楚。用户数据丢失是最难挽回的错误。发布前用 TestFlight 完整测一遍升级路径。


12. 其他持久化与同步方案

12.1 各方案的选择

需求方案
用户设置、开关、上次选择@AppStorage / UserDefaults
结构化业务数据、需要查询SwiftData
跨设备同步(同一 Apple 账号)SwiftData + CloudKit,或 NSUbiquitousKeyValueStore
多用户共享数据、服务端逻辑CloudKit(公共数据库)或自建后端
密码、令牌等敏感信息Keychain
文件(图片、文档、导出)FileManager + Documents 目录
一次性缓存URLCache / Caches 目录 / 内存

12.2 UserDefaults

swift
UserDefaults.standard.set(true, forKey: "hasSeenOnboarding")
let seen = UserDefaults.standard.bool(forKey: "hasSeenOnboarding")

// App 与小组件、扩展共享数据需要 App Group
let shared = UserDefaults(suiteName: "group.com.yourname.app")

12.3 文件系统

swift
let docs = URL.documentsDirectory              // 用户数据,会被备份
let caches = URL.cachesDirectory                // 缓存,系统可能清理
let temp = URL.temporaryDirectory               // 临时文件

let fileURL = docs.appending(path: "notes.json")
try data.write(to: fileURL)
let loaded = try Data(contentsOf: fileURL)

// JSON 编解码
let encoder = JSONEncoder()
encoder.outputFormatting = .prettyPrinted
encoder.dateEncodingStrategy = .iso8601
let json = try encoder.encode(todos)

12.4 CloudKit

Apple 的托管后端。对独立开发者极有价值:不用管服务器、不用管数据库运维、免费额度慷慨、用户用 Apple 账号自动登录。

三种数据库:

  • Private:用户自己的数据,只有他能访问。SwiftData 的 iCloud 同步用的就是这个。
  • Shared:用户主动分享给别人的数据。
  • Public:所有用户可见(比如排行榜、公共内容库)。

最省事的用法就是 SwiftData 开 cloudKitDatabase: .automatic。需要精细控制时用 CloudKit 原生 API:

swift
import CloudKit

let container = CKContainer.default()
let db = container.publicCloudDatabase

let record = CKRecord(recordType: "Article")
record["title"] = "标题"
try await db.save(record)

let query = CKQuery(recordType: "Article",
                    predicate: NSPredicate(format: "title CONTAINS %@", "Swift"))
let (results, _) = try await db.records(matching: query)

注意事项:CloudKit + SwiftData 同步要求所有属性有默认值或为可选、关系必须是可选的、不能用 @Attribute(.unique)。开发时在 Xcode 里勾选 iCloud capability 并在 CloudKit Dashboard 里部署 schema 到生产环境。

12.5 Keychain

存密码、令牌、密钥。系统加密,App 删除后可选保留。

swift
import Security

func saveToken(_ token: String) throws {
    let query: [String: Any] = [
        kSecClass as String: kSecClassGenericPassword,
        kSecAttrAccount as String: "apiToken",
        kSecValueData as String: Data(token.utf8)
    ]
    SecItemDelete(query as CFDictionary)
    let status = SecItemAdd(query as CFDictionary, nil)
    guard status == errSecSuccess else { throw KeychainError.saveFailed }
}

Keychain 的 C 风格 API 很难用。实践中通常自己封一层,或用一个极小的第三方包。

12.6 SwiftUI 文档型 App

如果你的 App 是「打开/编辑/保存文件」型的(文本编辑器、绘图工具、笔记),用 DocumentGroup

swift
@main
struct MyDocApp: App {
    var body: some Scene {
        DocumentGroup(newDocument: TextDocument()) { file in
            EditorView(document: file.$document)
        }
    }
}
iOS 27 大幅增强
新的 Document API 提供 WritableDocument / ReadableDocument 协议,支持异步、增量的磁盘读写和通过 Foundation Subprogress 汇报进度——大文件不再需要一次性读进内存。DocumentCreationSource + NewDocumentButton 允许声明多种新建来源(从空白、从模板、从导入)。做文档类 App 现在体验好了非常多。

13. 网络与后端

13.1 URLSession

swift
struct APIClient {
    let baseURL = URL(string: "https://api.example.com")!

    func fetch<T: Decodable>(_ path: String, as type: T.Type) async throws -> T {
        let url = baseURL.appending(path: path)
        let (data, response) = try await URLSession.shared.data(from: url)

        guard let http = response as? HTTPURLResponse else {
            throw APIError.invalidResponse
        }
        guard (200..<300).contains(http.statusCode) else {
            throw APIError.server(code: http.statusCode)
        }

        let decoder = JSONDecoder()
        decoder.keyDecodingStrategy = .convertFromSnakeCase
        decoder.dateDecodingStrategy = .iso8601
        return try decoder.decode(T.self, from: data)
    }

    func post<Body: Encodable, T: Decodable>(
        _ path: String, body: Body, as type: T.Type
    ) async throws -> T {
        var request = URLRequest(url: baseURL.appending(path: path))
        request.httpMethod = "POST"
        request.setValue("application/json", forHTTPHeaderField: "Content-Type")
        request.httpBody = try JSONEncoder().encode(body)

        let (data, _) = try await URLSession.shared.data(for: request)
        return try JSONDecoder().decode(T.self, from: data)
    }
}

其他常用能力:

swift
// 下载文件到磁盘(不占内存)
let (fileURL, _) = try await URLSession.shared.download(from: url)

// 上传
let (data, _) = try await URLSession.shared.upload(for: request, from: fileData)

// 流式读取(逐行)
for try await line in url.lines { print(line) }

// 后台下载(App 退到后台也继续)
let config = URLSessionConfiguration.background(withIdentifier: "com.app.download")

你不需要 AlamofireURLSession + async/await 已经足够简洁,少一个依赖就少一份维护负担。

13.2 网络状态与连接

swift
import Network

let monitor = NWPathMonitor()
monitor.pathUpdateHandler = { path in
    print(path.status == .satisfied ? "有网" : "没网")
    print(path.isExpensive ? "蜂窝网络" : "WiFi")
}
monitor.start(queue: .global())

13.3 后端选择

作为独立开发者,后端策略按成本从低到高:

  1. 无后端:数据全在本地 + iCloud 同步。绝大多数工具类 App 属于这一类,优先考虑
  2. CloudKit:需要跨用户共享或公共内容时。零运维、包含在开发者账号里。
  3. BaaS(Supabase / Firebase 等):需要关系型数据库、实时订阅、第三方登录时。
  4. 自建 Swift 后端VaporHummingbird。好处是前后端共用一套 Swift 类型定义。
WWDC26 消息:Foundation Models 框架将来会开源,意味着你在 App 里写的同一套 Swift AI 代码可以直接跑在服务端。同时 Apple 用 Swift 重写了网络栈的 QUIC 层并开源(swift-nio-quic),Swift 服务端生态在持续加强。

13.4 推送通知

swift
import UserNotifications

// 请求权限
let granted = try await UNUserNotificationCenter.current()
    .requestAuthorization(options: [.alert, .badge, .sound])

// 本地通知
let content = UNMutableNotificationContent()
content.title = "该喝水了"
content.body = "已经两小时没喝水了"
content.sound = .default

let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 7200, repeats: true)
let request = UNNotificationRequest(identifier: UUID().uuidString,
                                    content: content, trigger: trigger)
try await UNUserNotificationCenter.current().add(request)

远程推送(APNs)需要服务端。轻量方案:用 CloudKit 的订阅机制(CKSubscription)实现「数据变化时推送」,完全不需要自己的服务器。


第五部分 · 智能

14. Foundation Models

这是 Apple 目前最值得独立开发者关注的框架。它提供设备端大语言模型的原生 Swift API,无需 API Key、无 token 成本、数据不出设备。

14.1 基础用法

swift
import FoundationModels

let session = LanguageModelSession()
let response = try await session.respond(to: "用一句话总结这段文字:\(text)")
print(response.content)

带指令的会话(相当于 system prompt):

swift
let session = LanguageModelSession(
    instructions: """
    你是一个记账助手。用户会描述一笔消费,
    你需要提取金额、类别和商家。回答保持简洁。
    """
)

流式输出:

swift
for try await partial in session.streamResponse(to: prompt) {
    displayText = partial.content     // 逐字更新界面
}

14.2 结构化输出:@Generable

让模型直接返回类型安全的 Swift 结构体,而不是要你解析字符串。这是这个框架最好用的部分。

swift
@Generable
struct Expense {
    @Guide(description: "消费金额,单位为元")
    let amount: Double

    @Guide(description: "消费类别", .anyOf(["餐饮", "交通", "购物", "娱乐", "其他"]))
    let category: String

    @Guide(description: "商家名称,如果无法确定则留空")
    let merchant: String

    @Guide(description: "3-5 个描述这笔消费的标签", .count(3...5))
    let tags: [String]
}

let session = LanguageModelSession()
let expense = try await session.respond(
    to: "今天在星巴克花了 38 块买咖啡",
    generating: Expense.self
).content

print(expense.amount)     // 38.0
print(expense.category)   // "餐饮"

@Guide 的约束会被编译成对模型解码过程的硬约束——模型不可能输出不符合类型的内容。这比「让 GPT 返回 JSON 然后祈祷它格式正确」可靠得多。

14.3 工具调用(Tool Calling)

让模型能调用你 App 里的函数:

swift
struct SearchNotesTool: Tool {
    let name = "searchNotes"
    let description = "在用户的笔记中搜索包含关键词的内容"

    @Generable
    struct Arguments {
        @Guide(description: "搜索关键词")
        let query: String
    }

    func call(arguments: Arguments) async throws -> String {
        let results = await NoteStore.shared.search(arguments.query)
        return results.map(\.title).joined(separator: "\n")
    }
}

let session = LanguageModelSession(
    tools: [SearchNotesTool()],
    instructions: "你可以搜索用户的笔记来回答问题。"
)
let answer = try await session.respond(to: "我上周关于设计的笔记里写了什么?")

模型会自动决定何时调用工具、把结果整合进回答。

14.4 WWDC26 的重大更新

① 任意模型提供方。新增 LanguageModel 协议,LanguageModelSession 现在可以由本地模型或云端模型驱动:

  • Apple 设备端模型(默认)
  • Private Cloud Compute 上的新一代 Apple 模型
  • Claude、Gemini 等云端模型(Anthropic 和 Google 都发布了对应的 Swift 包)
  • CoreAILanguageModelMLXLanguageModel(开源实现,跑你自己的模型)

意义在于:同一套代码,切换模型只改一行。开发时用云端大模型验证效果,上线时降级到设备端模型省成本;或者根据设备能力动态选择。

② Private Cloud Compute 免费额度。Apple 的表述是:加入 App Store 小型企业计划、且 App 累计首次下载量低于 200 万,可以零云端 API 成本使用 PCC 上的新一代 Apple 基础模型。这对独立开发者是实打实的补贴。

不过资格判定的主语(是按单个 App 算,还是按开发者账号下所有 App 合计)Apple 在不同页面的措辞略有出入,接近门槛时请以 Private Cloud Compute 页面的原文为准。

③ 多模态输入。可以在 prompt 里传入图片,模型能推理视觉内容。Vision 框架的 OCR、条码识别可以作为工具直接被模型调用,全程在设备上。

④ Dynamic Profiles。在一个连续会话中动态切换模型、工具和指令,让 App 行为随上下文变化。

⑤ Evaluations 框架。专门用来验证 AI 功能的可靠性——传统单元测试无法覆盖「模型输出质量」,Evaluations 提供了系统化的评测方法,可以对 prompt 做「爬山式」迭代优化。还有 Instruments 支持,可以剖析 agent 行为。

⑥ fm CLI 与 Python SDK。可以在命令行和 Python 里调用同一套能力,方便做数据处理脚本。

14.5 实践建议

  • 先问「这个功能真的需要 LLM 吗?」 很多需求用正则、NaturalLanguage 框架或者一个下拉菜单就能解决,而且更快更可靠。
  • 设备端模型参数量不大。它擅长:摘要、分类、改写、提取、简短对话。不擅长:复杂推理、长文创作、精确计算、冷门知识。
  • 检查可用性:SystemLanguageModel.default.availability —— 老设备或未开启 Apple Intelligence 时要有降级方案。
  • @Generable 而不是让模型输出 JSON 再解析。
  • 给用户对 AI 输出的控制权:可编辑、可撤销、明确标注这是 AI 生成的。

15. App Intents 与系统集成

App Intents 让你的 App 功能可以被系统调用——Siri、快捷指令、聚焦搜索、小组件、控制中心、操作按钮,都走这一套。

对独立开发者的价值:这是让 App 深度嵌入系统、跟只有一个图标的普通 App 拉开差距的最有效手段,而且成本不高。

15.1 定义一个 Intent

swift
import AppIntents

struct AddTodoIntent: AppIntent {
    static let title: LocalizedStringResource = "添加待办"
    static let description = IntentDescription("在待办列表中新增一项")

    @Parameter(title: "内容")
    var text: String

    func perform() async throws -> some IntentResult & ProvidesDialog {
        await TodoStore.shared.add(text)
        return .result(dialog: "已添加:\(text)")
    }
}

15.2 暴露给 Siri

swift
struct MyAppShortcuts: AppShortcutsProvider {
    static var appShortcuts: [AppShortcut] {
        AppShortcut(
            intent: AddTodoIntent(),
            phrases: [
                "在 \(.applicationName) 里添加待办",
                "用 \(.applicationName) 记一件事"
            ],
            shortTitle: "添加待办",
            systemImageName: "plus.circle"
        )
    }
}

15.3 App Entity让系统理解你的数据

swift
struct TodoEntity: AppEntity {
    let id: UUID
    let title: String

    static let typeDisplayRepresentation: TypeDisplayRepresentation = "待办"
    var displayRepresentation: DisplayRepresentation { .init(title: "\(title)") }

    static let defaultQuery = TodoQuery()
}

struct TodoQuery: EntityQuery {
    func entities(for ids: [UUID]) async throws -> [TodoEntity] {
        await TodoStore.shared.todos(ids: ids).map(TodoEntity.init)
    }
    func suggestedEntities() async throws -> [TodoEntity] {
        await TodoStore.shared.recent().map(TodoEntity.init)
    }
}

15.4 WWDC26 的更新

① App Schemas。Apple 预定义了一批标准的实体 schema意图 schema。你把自己的数据映射到这些 schema 上,就自动获得:

  • 内容进入 Spotlight 的语义索引,Siri 可以检索到并注明来自你的 App
  • 用户可以用任意自然语言对内容执行操作——你不需要预定义短语,Siri 的语言理解升级或支持新语言时你不需要改代码

这是一个重要转变:从「我告诉系统我支持哪些说法」变成「我告诉系统我有什么数据和能力,剩下的交给系统」。

② View Annotations API。把界面上的视图映射到实体,用户就能对着屏幕上看到的东西用自然语言下指令(「把这个加到收藏」)。

③ AppIntentsTesting 框架。可以走真实的系统路径验证集成是否正确,不用写 UI 自动化测试。以前 App Intents 最难受的就是「不知道有没有接对」,现在可测了。

15.5 相关的系统集成点

能力框架 / API
Spotlight 索引CoreSpotlight,WWDC26 起支持 LLM 语义搜索
通用链接 / Deep LinkAssociated Domains + .onOpenURL
分享到你的 AppShare Extension
从你的 App 分享ShareLink / Transferable
拖放.draggable / .dropDestination
后台任务BackgroundTasks
快捷指令自动化App Intents(自动获得)
控制中心控件ControlWidget(见 18.4)
操作按钮 / 相机控制App Intents(自动获得)

16. Core AI、Core ML 与感知框架

16.1 Core AI(WWDC26 新增)

Core AI 是内置于系统、专为 Apple Silicon 打造的自带模型运行时。定位是:Foundation Models 解决「用 Apple 的模型」,Core AI 解决「用你自己的模型」。

特点:

  • 现代的、内存安全的 Swift API,加载 / 特化 / 运行全在设备上
  • 模型自动针对当前硬件特化,支持提前编译(AOT)加快加载
  • 精细的推理内存控制、零拷贝数据通路、有状态执行
  • 覆盖从小型视觉模型到大规模生成式模型
  • 提供 CoreAILanguageModel,可以直接插进 Foundation Models 框架当作模型后端
  • 复用熟悉的 Python / PyTorch 工作流做模型创作、优化和转换

零服务端依赖、零 token 成本。对想做「本地跑自己训练的模型」的开发者,这是新的主干道。

16.2 Core ML

仍然是通用机器学习模型的部署框架。适合分类、检测、风格迁移这类传统 ML 任务。

swift
let model = try MyImageClassifier(configuration: MLModelConfiguration())
let output = try await model.prediction(image: pixelBuffer)
print(output.classLabel, output.classLabelProbs)

配套的 Create ML 是图形化训练工具(Xcode → Open Developer Tool → Create ML),不用写代码就能训练图像分类、目标检测、声音分类、动作分类、表格回归等模型。对独立开发者门槛极低。

16.3 MLX

Apple 开源的、面向 Apple Silicon 的数组计算与训练框架,用于实验、训练、微调大模型。WWDC26 加入了 Metal 4 和 GPU 神经加速器支持,并支持通过 Thunderbolt 上的 RDMA 在多台 Mac 之间分布式训练

如果你只是「用模型」,用 Foundation Models 或 Core AI;如果你要「改模型」,用 MLX。

16.4 Vision图像理解

swift
import Vision

// 文字识别(OCR)
let request = RecognizeTextRequest()
let results = try await request.perform(on: image)
for observation in results {
    print(observation.topCandidates(1).first?.string ?? "")
}

Vision 提供的能力:文字识别、条码识别、人脸检测与特征点、人体姿态、手部姿态、物体轨迹、显著性分析(智能裁剪)、图像相似度、文档扫描。全部在设备上完成。

WWDC26 起,Vision 的 OCR 和条码识别可以作为工具直接暴露给 Foundation Models 的模型调用。watchOS 27 也开始支持 Vision。

16.5 其他感知与语言框架

框架能力
Speech语音转文字,支持设备端识别
AVSpeechSynthesizer文字转语音
Translation系统级翻译,可提供 UI 也可纯 API 调用
NaturalLanguage分词、词性标注、命名实体识别、语言识别、情感分析、词向量
SoundAnalysis声音事件分类
Music UnderstandingWWDC26 新增,在设备上从六个维度分析音频
VisionKit现成的 UI 组件:文档扫描、实况文本、视觉查找

NaturalLanguage 值得单独提一句:很多「需要 AI」的需求(关键词提取、语言检测、粗粒度情感判断)用它就够了,速度快几个数量级且完全确定。


第六部分 · 系统能力

17. 系统能力框架全景

这一章是目录性质的。不要通读,需要什么功能时来这里找入口。

17.1 位置与地图

swift
import MapKit
import CoreLocation

// 地图(SwiftUI 原生)
Map {
    Marker("公司", coordinate: officeCoordinate)
    Annotation("家", coordinate: homeCoordinate) {
        Image(systemName: "house.fill")
    }
    MapPolyline(coordinates: route)
        .stroke(.blue, lineWidth: 4)
}
.mapStyle(.standard(elevation: .realistic))
.mapControls { MapUserLocationButton(); MapCompass() }

// 定位
let manager = CLLocationManager()
manager.requestWhenInUseAuthorization()

// 搜索地点
let request = MKLocalSearch.Request()
request.naturalLanguageQuery = "咖啡店"
let response = try await MKLocalSearch(request: request).start()

相关:MapKit(地图与搜索)、CoreLocation(定位、地理围栏、信标)、Contacts(地址簿)。

17.2 健康与运动

swift
import HealthKit

let store = HKHealthStore()
try await store.requestAuthorization(toShare: [], read: [
    HKQuantityType(.stepCount), HKQuantityType(.heartRate)
])

let descriptor = HKSampleQueryDescriptor(
    predicates: [.quantitySample(type: HKQuantityType(.stepCount))],
    sortDescriptors: [SortDescriptor(\.startDate, order: .reverse)],
    limit: 100
)
let samples = try await descriptor.result(for: store)

WWDC26 新增(两项均出自 Apple 的 WWDC26 watchOS guide):

相关:HealthKitWorkoutKit(生成训练计划并推送到 Apple Watch)、CoreMotion(加速度计、陀螺仪、计步)。

17.3 天气

swift
import WeatherKit

let weather = try await WeatherService.shared.weather(for: location)
print(weather.currentWeather.temperature)
print(weather.dailyForecast.forecast.first?.highTemperature ?? "")

每月 50 万次调用包含在开发者账号里,超出付费。注意必须按要求展示 Apple Weather 的署名与链接。

17.4 相机、照片与媒体

需求框架
让用户选照片PhotosPicker(SwiftUI 原生,无需相册权限)
读写相册PhotoKit
自定义相机AVFoundationAVCaptureSession
系统相机拍照界面UIImagePickerController 桥接(或自己用 AVCaptureSession 搭)
生成式图像ImagePlayground(Apple Intelligence,不是相机/选图)
播放视频VideoPlayer(SwiftUI)/ AVKit
音频播放录制AVFoundation / AVAudioEngine
图像处理与滤镜Core Image
播放控制中心集成`NowPlaying` 框架(WWDC26 新增)
swift
// 选照片
@State private var item: PhotosPickerItem?
PhotosPicker("选择照片", selection: $item, matching: .images)
    .onChange(of: item) { _, new in
        Task {
            if let data = try? await new?.loadTransferable(type: Data.self) {
                image = UIImage(data: data)
            }
        }
    }

// 播放视频
VideoPlayer(player: AVPlayer(url: videoURL))

WWDC26 更新:NowPlaying 框架把播放状态统一接入锁屏、控制中心、灵动岛和 CarPlay;Core Image 的 RAW 处理升到第 9 版,锐度和色彩明显提升;新增自动生成字幕与字幕样式。

17.5 音乐

  • MusicKit —— 访问 Apple Music 目录、播放列表、用户库
  • ShazamKit —— 音乐识别,也支持匹配你自己的音频指纹库
  • Music Understanding(WWDC26 新增)—— 设备端六维度音频分析

17.6 支付与钱包

  • StoreKit —— App 内购买与订阅(见第 22 章)
  • PassKit —— Apple Pay、钱包卡券
  • Wallet 相关扩展 —— 会员卡、票券、门禁卡

17.7 认证与安全

swift
// Sign in with Apple
SignInWithAppleButton(.signIn) { request in
    request.requestedScopes = [.fullName, .email]
} onCompletion: { result in
    // 处理结果
}
.signInWithAppleButtonStyle(.black)

// 生物识别
import LocalAuthentication
let context = LAContext()
let ok = try await context.evaluatePolicy(
    .deviceOwnerAuthenticationWithBiometrics,
    localizedReason: "解锁你的笔记"
)

相关:AuthenticationServices(Sign in with Apple、Passkeys 通行密钥、密码自动填充)、LocalAuthentication(Face ID / Touch ID)、CryptoKit(加解密、签名、哈希)。

推荐 Passkeys:无密码登录,安全性和体验都优于密码,Apple 生态支持完善。

17.8 隐私与家长控制

Apple 对这块的要求逐年加强,独立开发者要跟上:

框架 / 要求说明
Privacy Manifest必须声明 App 及所用 SDK 收集的数据类型和 API 使用原因
AppTrackingTransparency跨 App 追踪必须先弹窗获得许可
DeclaredAgeRange从系统获取用户年龄区间(不暴露具体生日),用于提供年龄适宜的内容
PermissionKit儿童向请求家长批准的统一流程
FamilyControls / ManagedSettings / DeviceActivity屏幕使用时间类 App 的官方框架
Time AllowancesiOS 27 新增。系统按类别(娱乐、游戏、社交媒体)给家长提供时长管理。2026 年 9 月起,提交新版本必须在年龄分级问卷中声明 App 是否具备社交媒体能力。

独立开发者必做:确认自己的 App 是否触及「社交媒体能力」定义(能让用户内容通过信息流或类似机制扩散给很多用户)。若是,会被归入社交媒体时长类别并至少 13+ 分级。

17.9 设备与连接

需求框架
蓝牙外设CoreBluetooth
NFC 标签CoreNFC
网络状态与自定义协议Network
局域网设备发现Network(Bonjour)
配件通信ExternalAccessory / AccessorySetupKit
智能家居HomeKit / Matter
触觉反馈CoreHaptics / .sensoryFeedback()
屏幕镜像与投屏AVRoutePickerView / AirPlay

17.10 系统服务

需求框架
本地与远程通知UserNotifications
后台任务BackgroundTasks
大文件分发`BackgroundAssets`(可自托管;选 Apple-Hosted 时开发者账号为每个 App 提供 200GB 托管容量)
日历与提醒EventKit
通讯录Contacts
文件选择.fileImporter / .fileExporter(SwiftUI 原生)
一起看/一起玩GroupActivities(SharePlay)
应用内评分请求StoreKitrequestReview
剪贴板UIPasteboard / PasteButton(SwiftUI)
重要变更
On-Demand Resources 在 iOS 27 / iPadOS 27 / tvOS 27 / visionOS 27 起正式标记为废弃,请迁移到 Apple-Hosted Background Assets。Apple 从 WWDC25 / iOS 26 起就在推这条迁移路径,iOS 27 只是 deprecation 的落点,不是第一次提出。已有 App 短期内仍可运行,但建议尽早规划迁移。

18. 小组件、实时活动与控件

这一组框架的共同点:你的 App 出现在 App 之外的地方。对独立开发者是低成本、高感知度的差异化手段。

18.1 WidgetKit 基础

swift
import WidgetKit
import SwiftUI

struct TodoEntry: TimelineEntry {
    let date: Date
    let pendingCount: Int
}

struct TodoProvider: TimelineProvider {
    func placeholder(in context: Context) -> TodoEntry {
        TodoEntry(date: .now, pendingCount: 3)
    }
    func getSnapshot(in context: Context, completion: @escaping (TodoEntry) -> Void) {
        completion(TodoEntry(date: .now, pendingCount: currentCount()))
    }
    func getTimeline(in context: Context, completion: @escaping (Timeline<TodoEntry>) -> Void) {
        let entry = TodoEntry(date: .now, pendingCount: currentCount())
        completion(Timeline(entries: [entry], policy: .after(.now.addingTimeInterval(3600))))
    }
}

struct TodoWidgetView: View {
    let entry: TodoEntry
    var body: some View {
        VStack {
            Text("\(entry.pendingCount)").font(.largeTitle.bold())
            Text("待办").font(.caption)
        }
        .containerBackground(.fill.tertiary, for: .widget)
    }
}

@main
struct TodoWidget: Widget {
    var body: some WidgetConfiguration {
        StaticConfiguration(kind: "TodoWidget", provider: TodoProvider()) { entry in
            TodoWidgetView(entry: entry)
        }
        .configurationDisplayName("待办统计")
        .supportedFamilies([.systemSmall, .systemMedium, .accessoryCircular])
    }
}

要点:

  • 小组件是独立的 Extension Target,跟主 App 通过 App Group 共享数据
  • 小组件视图是静态快照,不能有实时交互动画
  • 交互只能通过 App Intents(按钮、Toggle)
  • 时间线更新有系统预算,不能高频刷新

支持的位置:主屏幕、锁屏、待机模式(StandBy)、Mac 桌面与通知中心、Apple Watch 表盘复杂功能。

iOS 27 新增
小组件可以通过 App Intents 让用户自定义,并支持动态样式。

18.2 交互式小组件

swift
struct ToggleTodoIntent: AppIntent {
    static let title: LocalizedStringResource = "切换完成状态"
    @Parameter var todoID: String

    func perform() async throws -> some IntentResult {
        await TodoStore.shared.toggle(id: todoID)
        return .result()
    }
}

// 在小组件视图里
Button(intent: ToggleTodoIntent(todoID: todo.id)) {
    Image(systemName: todo.isDone ? "checkmark.circle.fill" : "circle")
}

18.3 Live Activities(实时活动)

灵动岛和锁屏上的实时状态:外卖配送、比赛比分、计时器、打车。

swift
import ActivityKit

struct DeliveryAttributes: ActivityAttributes {
    struct ContentState: Codable, Hashable {
        var status: String
        var estimatedMinutes: Int
    }
    let orderNumber: String
}

// 启动
let activity = try Activity.request(
    attributes: DeliveryAttributes(orderNumber: "12345"),
    content: .init(state: .init(status: "配送中", estimatedMinutes: 20), staleDate: nil)
)

// 更新
await activity.update(.init(state: .init(status: "即将送达", estimatedMinutes: 3), staleDate: nil))

// 结束
await activity.end(nil, dismissalPolicy: .immediate)

界面用 ActivityConfiguration 定义锁屏视图和灵动岛的紧凑/展开态。可以通过推送远程更新。

18.4 Controls(控制中心控件)

iOS 18 引入。你的功能可以出现在控制中心、锁屏和操作按钮上。

swift
struct QuickAddControl: ControlWidget {
    var body: some ControlWidgetConfiguration {
        StaticControlConfiguration(kind: "QuickAdd") {
            ControlWidgetButton(action: AddTodoIntent()) {
                Label("快速添加", systemImage: "plus")
            }
        }
    }
}

也支持 ControlWidgetToggle(开关型)。


第七部分 · 图形与空间

19. 图形、动效与游戏

19.1 SwiftUI 内置绘图

大部分自定义图形不需要离开 SwiftUI:

swift
// 形状
Circle()
RoundedRectangle(cornerRadius: 12)
Capsule()

// 自定义形状
struct Triangle: Shape {
    func path(in rect: CGRect) -> Path {
        var p = Path()
        p.move(to: CGPoint(x: rect.midX, y: rect.minY))
        p.addLine(to: CGPoint(x: rect.maxX, y: rect.maxY))
        p.addLine(to: CGPoint(x: rect.minX, y: rect.maxY))
        p.closeSubpath()
        return p
    }
}

// Canvas —— 命令式高性能绘制(大量图元时用它)
Canvas { context, size in
    for i in 0..<1000 {
        context.fill(Path(ellipseIn: randomRect(in: size)), with: .color(.blue))
    }
}

// 渐变与效果
.background(LinearGradient(colors: [.blue, .purple], startPoint: .top, endPoint: .bottom))
MeshGradient(width: 3, height: 3, points: [...], colors: [...])
.blur(radius: 8)
.visualEffect { content, proxy in
    content.scaleEffect(proxy.frame(in: .global).minY / 1000)
}

19.2 Metal 着色器

SwiftUI 可以直接调用 Metal 着色器做自定义视觉效果:

swift
// Shaders.metal
[[ stitchable ]] half4 wave(float2 position, half4 color, float time) {
    // ...
}

// SwiftUI
Image("photo")
    .colorEffect(ShaderLibrary.wave(.float(time)))

三种入口:colorEffect(改颜色)、distortionEffect(改位置)、layerEffect(读取周围像素)。

iOS 27 新增
Compose advanced graphics effects with SwiftUI 这一场介绍了更强的图形效果组合能力,配合刷新后的材质系统。

19.3 2D 与 3D

需求推荐
2D 游戏 / 粒子效果SpriteKit
3D 内容(新项目`RealityKit`
3D 内容(老项目)SceneKit(已废弃,不要用于新项目)
底层图形 / 自定义渲染管线Metal
游戏中心、成就、排行榜GameKit
手柄支持GameController
空间音频PHASE

WWDC26 的游戏相关更新:Game Porting Toolkit 4 加入开源的代理式编码技能(agentic coding skills),帮你把 Metal 和 Apple 平台的最佳实践嵌入移植流程;新增 Steam 资源转换器;Unity 有了官方 StoreKit 插件。


20. RealityKit 与 visionOS

20.1 RealityKit 是什么

Apple 的现代 3D 引擎,跨 visionOS / iOS / macOS。采用 ECS(实体-组件-系统)架构:

  • Entity(实体):场景里的一个东西
  • Component(组件):给实体附加的数据(模型、碰撞、物理、音频…)
  • System(系统):每帧处理带特定组件的实体
swift
import RealityKit

struct ImmersiveView: View {
    var body: some View {
        RealityView { content in
            // 加载模型
            if let robot = try? await Entity(named: "Robot") {
                robot.position = [0, 1, -2]
                robot.components.set(InputTargetComponent())
                robot.generateCollisionShapes(recursive: true)
                content.add(robot)
            }

            // 程序化创建
            let sphere = ModelEntity(
                mesh: .generateSphere(radius: 0.1),
                materials: [SimpleMaterial(color: .blue, isMetallic: true)]
            )
            content.add(sphere)
        } update: { content in
            // 状态变化时更新
        }
        .gesture(
            TapGesture().targetedToAnyEntity().onEnded { value in
                value.entity.position.y += 0.1
            }
        )
    }
}

20.2 visionOS 的三种呈现方式

swift
@main
struct SpatialApp: App {
    var body: some Scene {
        // 1. 窗口 —— 平面界面,跟 iPad App 类似
        WindowGroup { ContentView() }

        // 2. 体积 —— 有深度的 3D 盒子,可以在共享空间里跟别的 App 共存
        WindowGroup(id: "volume") {
            ModelView()
        }
        .windowStyle(.volumetric)
        .defaultSize(width: 0.5, height: 0.5, depth: 0.5, in: .meters)

        // 3. 沉浸空间 —— 独占整个环境
        ImmersiveSpace(id: "immersive") {
            ImmersiveView()
        }
        .immersionStyle(selection: $style, in: .mixed, .progressive, .full)
    }
}

给独立开发者的建议:先把 iPad App 编译到 visionOS(很多情况下零改动就能跑),验证有没有价值,再逐步加入体积和沉浸内容。不要一上来就做全沉浸体验。

20.3 visionOS 27 / RealityKit 的新能力

  • 物理空间光照:虚拟光源可以照亮真实环境的表面
  • 投影纹理(Projective Textures):给聚光灯加纹理,模拟彩色玻璃投影、水下焦散
  • 实时布料模拟:旗帜、窗帘、衣物自然响应运动和交互
  • Reverb Mesh API:按环境材质建模声音吸收与散射,做真实的空间音频
  • 3D 高斯泼溅(Gaussian Splats):高效渲染真实物体的照片级扫描结果
  • Reality Composer Pro 3:Mac 上的 3D 内容创作工具,深度集成 Xcode,支持可视化脚本、生成式智能辅助生成资产,以及在 Vision Pro 上的实时预览(改材质、调动画立刻在设备上看到)
  • Spatial Preview 框架:Mac App 可以把空间照片、Apple Immersive Video、3D 内容直接推到 Vision Pro 的快速查看里预览,支持 USD 实时编辑和 SharePlay 协作
  • 增强的物体追踪:高帧率追踪、Create ML 扩展训练选项、公制空间位姿 API;同一份参考物体在 iOS 和 visionOS 上通用,无需重新训练(iOS 通过 ARKit 提供同等能力)
  • Foveated Streaming 框架(visionOS 26.4 引入):根据注视区域只串流必要区域的高质量内容
  • 空间配件:第三方可以做带 IR LED + IMU 的六自由度追踪配件,最高 90Hz

20.4 ARKit

在 iOS 上做增强现实,或在 visionOS 上获取环境理解数据(平面、场景网格、手部、世界追踪)。SwiftUI 里通常和 RealityKit 组合使用。


第八部分 · 平台与交付

21. 各平台差异速览

一份 SwiftUI 代码可以跑在所有平台上,但体验设计必须因平台而异

平台核心交互独有能力设计要点
iOS触摸、单手相机、传感器、Live Activity、灵动岛单列布局、大按钮、底部操作区
iPadOS触摸 + 键鼠 + Apple Pencil多窗口、侧拉、外接屏、PencilKit多栏布局、支持键盘快捷键、拖放
macOS键鼠菜单栏、多窗口、Settings 场景、命令行、后台常驻信息密度高、支持右键菜单和快捷键
watchOS表冠 + 触摸,极短时长复杂功能、锻炼会话、通知转发、WorkoutKit一屏一件事、大字号、依赖复杂功能入口
tvOS遥控器焦点导航TVUIKit、Top Shelf焦点驱动、10 尺观看距离、避免文字输入
visionOS眼动 + 手势沉浸空间、空间音频、物体追踪尊重用户物理环境、避免遮挡视野

跨平台代码组织:

swift
#if os(iOS)
    .navigationBarTitleDisplayMode(.inline)
#elseif os(macOS)
    .frame(minWidth: 600, minHeight: 400)
#endif

// 更好的方式:用尺寸类而不是平台判断
@Environment(\.horizontalSizeClass) private var sizeClass
if sizeClass == .compact { VStack { ... } } else { HStack { ... } }

macOS 特有场景:

swift
@main
struct MyApp: App {
    var body: some Scene {
        WindowGroup { ContentView() }
            .commands {                       // 菜单栏
                CommandGroup(after: .newItem) {
                    Button("导入…") { }.keyboardShortcut("i", modifiers: .command)
                }
            }

        Settings { SettingsView() }           // 偏好设置窗口

        MenuBarExtra("状态", systemImage: "star") {   // 菜单栏常驻
            StatusView()
        }
    }
}

关于 Intel:这里有两条独立的线,别混在一起。

  • 架构支持线:macOS 26 Tahoe 是最后一条支持 Intel Mac 的系统线,macOS 27(Golden Gate)只支持 Apple Silicon;Xcode 27 本身也只有 arm64 版本。
  • App Store 政策线:Apple 在 WWDC26 说明,Mac App Store 上以「通用购买」形式提供的 App 与游戏不再需要支持 Intel,前提是「你的 App 支持 macOS 13.0 或更高」(这是 Apple 原话里的前置条件,用于界定可以在 App Store Connect 里撤下 Intel 支持的范围)。

实际结论:作为独立开发者,你现在可以只出 Apple Silicon 版本,不必再维护通用二进制。


22. StoreKit 与变现

22.1 StoreKit 2

用 async/await 重写的现代 API,比第一代简洁得多。新项目直接用 StoreKit 2。

swift
import StoreKit

@MainActor
@Observable
final class Store {
    private(set) var products: [Product] = []
    private(set) var purchasedIDs: Set<String> = []

    private let productIDs = ["com.app.pro.monthly", "com.app.pro.yearly", "com.app.lifetime"]
    private var updateTask: Task<Void, Never>?

    init() {
        updateTask = Task { await observeTransactions() }
    }

    func loadProducts() async {
        products = (try? await Product.products(for: productIDs)) ?? []
    }

    func purchase(_ product: Product) async throws {
        let result = try await product.purchase()
        switch result {
        case .success(let verification):
            let transaction = try checkVerified(verification)
            await updateEntitlements()
            await transaction.finish()
        case .userCancelled, .pending:
            break
        @unknown default:
            break
        }
    }

    func restore() async throws {
        try await AppStore.sync()
        await updateEntitlements()
    }

    private func updateEntitlements() async {
        var ids: Set<String> = []
        for await result in Transaction.currentEntitlements {
            if let t = try? checkVerified(result) {
                ids.insert(t.productID)
            }
        }
        purchasedIDs = ids
    }

    // 监听在别处发生的交易(家庭共享、退款、续订)
    private func observeTransactions() async {
        for await result in Transaction.updates {
            if let t = try? checkVerified(result) {
                await updateEntitlements()
                await t.finish()
            }
        }
    }

    private func checkVerified<T>(_ result: VerificationResult<T>) throws -> T {
        switch result {
        case .verified(let safe): return safe
        case .unverified: throw StoreError.failedVerification
        }
    }
}

enum StoreError: Error { case failedVerification }

22.2 现成的 SwiftUI 商店界面

不想自己画付费墙,用系统组件:

swift
// 订阅页(Apple 提供的标准布局,自动本地化、自动处理购买流程)
SubscriptionStoreView(groupID: "YOUR_GROUP_ID") {
    VStack {
        Image(systemName: "star.circle.fill").font(.system(size: 60))
        Text("升级到 Pro").font(.largeTitle.bold())
    }
}
.storeButton(.visible, for: .restorePurchases)
.subscriptionStorePolicyDestination(url: privacyURL, for: .privacyPolicy)

// 单个商品
ProductView(id: "com.app.lifetime")

// 商品列表
StoreView(ids: productIDs)

22.3 测试内购

StoreKit Configuration 文件在本地测试,不需要连 App Store:

  1. File → New → File → StoreKit Configuration File
  2. 在里面配置商品、价格、订阅组
  3. Scheme → Edit Scheme → Run → Options → StoreKit Configuration 选中它
  4. 可以模拟购买成功、失败、订阅续订加速、退款等场景

正式测试用 Sandbox 账号(App Store Connect 里创建)+ TestFlight。

22.4 WWDC26 的订阅新玩法

对独立开发者影响较大的几项:

能力说明时间
Retention Messaging用户点取消订阅时展示挽留信息 + 专属优惠,且不增加取消流程的阻力。可在 App Store Connect 配置,也有 API 实时交互今秋
订阅捆绑(Bundles / Suites)Bundle:一次购买多个已有订阅;Suite:一组不单独售卖、打包成一个订阅出售今夏公布细节
群组购买(Group Purchases)用户一次买多个席位,再邀请别人加入。Apple 提供完整邀请流程今年晚些
批量购买(Volume Purchasing)通过 Apple School Manager / Apple Business Manager 卖给企业和学校今秋
月付 + 12 个月承诺更低月费换取一年承诺,用户可在 Apple 账户查看已付/剩余期数已上线(需 iOS 26.4+,美国和新加坡除外)
统一的内购提审流程多个内购、订阅、活动、自定义产品页可以打包成一次提交今夏

22.5 定价策略(独立开发者视角)

这里只陈述常见做法和取舍,具体选择取决于你的产品:

  • 一次性买断:用户接受度高、无持续收入。适合工具型、功能边界清晰的 App。
  • 订阅:有持续收入支撑长期维护,但需要持续交付价值,否则流失率高。适合有服务器成本、内容持续更新、或功能持续增长的 App。
  • 免费 + 内购解锁:转化路径最短。关键是免费部分要真的有用,而不是残废版。
  • 付费下载:转化率低,除非有很强的口碑或媒体曝光。

App Store 小型企业计划:年收入低于 100 万美元的开发者,抽成从 30% 降到 15%。记得主动申请,不会自动生效。WWDC26 起,加入该计划且累计首次下载低于 200 万的 App 还能免费用 Private Cloud Compute 上的 Apple 基础模型。


23. 测试、调试与发布

23.1 Swift Testing

Apple 新的官方单元测试框架,新项目的推荐选择,语法比 XCTest 简洁得多。但 XCTest 并没有消失:UI 测试仍然必须用 XCUITest,遗留项目里的 XCTest 也可以继续跑——Swift 6.4 起两者支持互操作,可以在同一个测试 target 里共存,渐进迁移。

swift
import Testing
@testable import MyApp

@Test func 添加待办后数量增加() {
    let store = TodoStore()
    store.add("买牛奶")
    #expect(store.todos.count == 1)
    #expect(store.todos.first?.title == "买牛奶")
}

@Test("空标题应该被拒绝")
func 拒绝空标题() throws {
    let store = TodoStore()
    #expect(throws: ValidationError.self) {
        try store.addValidated("")
    }
}

// 参数化测试:一次跑多组数据
@Test(arguments: [("", false), ("a", true), (String(repeating: "x", count: 200), false)])
func 标题校验(input: String, expected: Bool) {
    #expect(Todo.isValidTitle(input) == expected)
}

// 分组与标签
@Suite("待办存储")
struct TodoStoreTests {
    @Test(.tags(.critical)) func 持久化() async throws { ... }
}

#expect 失败时会展示表达式中每个子项的实际值,调试信息比 XCTAssert 好得多。Swift 6.4 起支持与 XCTest 互操作,老项目可以渐进迁移。

UI 测试目前仍用 XCUITest。

23.2 调试

  • 断点:点击行号。右键可以加条件、加日志(不中断执行)。
  • `print` 与 `dump`dump(object) 会打印完整结构。
  • 视图层级调试:运行中点击 Debug View Hierarchy 图标,可以 3D 分解界面。
  • Instruments(⌘I):性能剖析。常用模板:Time Profiler(CPU)、Allocations(内存)、Leaks(泄漏)、SwiftUI(视图重绘次数)、Animation Hitches(掉帧)。
  • WWDC26 起,Instruments 支持剖析 Foundation Models 的 agent 行为。
  • `.background(.red)` 大法:布局出问题时给可疑视图加个颜色背景,立刻能看清它占了多大。
  • `Self._printChanges()`:放在 body 里,打印是什么导致了这次重绘。排查 SwiftUI 性能问题的利器。

23.3 Xcode 27 的工具更新

  • 编码代理:可选择模型,可用官方 SwiftUI agent skills 引导它写出符合当年最佳实践的代码
  • Device Hub:所有设备统一管理
  • 神经网络引擎驱动的代码补全
  • 构建速度提升ViewBuilder 的改动(现在以 ContentBuilder 形式暴露)显著改善 SwiftUI 项目的编译时间
  • 本地化工作流增强

23.4 发布流程

1. App Store Connect 创建 App 记录(Bundle ID 要和 Xcode 里一致)
2. 准备素材:图标、截图(各尺寸)、描述、关键词、隐私标签、年龄分级
3. Xcode: Product → Archive → Distribute App → App Store Connect
4. 在 App Store Connect 里把构建版本分配给 TestFlight
5. TestFlight 内测(内部最多 100 人,外部最多 10000 人,外部需简单审核)
6. 收集反馈、修复、迭代
7. 提交审核(通常 24-48 小时)
8. 通过后手动或自动发布

独立开发者的实用建议

  • 截图是转化率的第一影响因素,比 App 本身的介绍文字重要得多。花时间做好。
  • WWDC26 起 App Store Connect 有了 Asset Library(集中管理所有视觉素材,可独立于版本提审)和产品页预览工具(发布前看到实际效果)。今秋上线。
  • 同时新增了产品页头图、搜索结果配图等更多展示位。
  • 隐私标签要如实填写。填错被发现会下架。
  • 首次提交容易被拒的原因:功能不完整、有占位内容、崩溃、内购没有恢复购买按钮、隐私政策链接失效、要求登录但没提供测试账号。
  • 准备一个演示账号给审核员,写在「审核备注」里。
  • Xcode Cloud 可以自动化构建和分发到 TestFlight,开发者账号含一定免费额度。对一个人的团队来说,省掉的是「每次发版手动 Archive 二十分钟」。

附录

附录 A. 框架速查表

按「我要做什么」检索。

界面

需求框架
全平台界面SwiftUI
图表Swift Charts
功能引导提示TipKit
小组件 / 复杂功能WidgetKit
实时活动 / 灵动岛ActivityKit
控制中心控件WidgetKit(ControlWidget)
手写与绘图PencilKit
富文本编辑SwiftUI TextEditor + AttributedString
Web 内容WebKit
数据观察Observation

数据

需求框架
本地数据库SwiftData
设置项@AppStorage / UserDefaults
iCloud 同步SwiftData + CloudKit
后端服务CloudKit
敏感信息Keychain(Security)
文件与文档FileManager / DocumentGroup
网络URLSession / Network

智能

需求框架
设备端 LLMFoundationModels
自带模型推理Core AI
AI 功能评测Evaluations
传统 ML 模型Core ML / Create ML
模型训练与微调MLX
图像理解Vision / VisionKit
语音识别Speech
翻译Translation
文本分析NaturalLanguage
音频分析SoundAnalysis / Music Understanding
系统集成与 SiriApp Intents
搜索索引CoreSpotlight

系统能力

需求框架
地图与定位MapKit / CoreLocation
健康与运动HealthKit / WorkoutKit / CoreMotion
天气WeatherKit
相机与媒体AVFoundation / PhotoKit / Core Image
播放控制集成NowPlaying
音乐MusicKit / ShazamKit
通知UserNotifications
后台任务BackgroundTasks / BackgroundAssets
日历与通讯录EventKit / Contacts
支付StoreKit / PassKit
登录与安全AuthenticationServices / LocalAuthentication / CryptoKit
隐私与家长控制AppTrackingTransparency / DeclaredAgeRange / PermissionKit / FamilyControls
蓝牙 / NFC / 配件CoreBluetooth / CoreNFC / AccessorySetupKit
智能家居HomeKit / Matter
触觉反馈CoreHaptics
一起看/一起玩GroupActivities

图形与空间

需求框架
3D 内容RealityKit + Reality Composer Pro
2D 游戏SpriteKit
底层图形Metal
环境理解 / ARARKit
空间内容预览Spatial Preview
注视点串流Foveated Streaming
游戏服务GameKit / GameController
空间音频PHASE

工程

需求工具
单元测试Swift Testing
UI 测试XCUITest
依赖管理Swift Package Manager
文档生成DocC
性能剖析Instruments
CI/CDXcode Cloud

附录 B. 避坑清单

先说清楚这张表的性质:它是给新手的学习路线取舍,不是「左边的技术已经作废」。左列大多仍在系统里正常运行、仍有大量存量代码和第三方 SDK 依赖它们;有些场景(UI 测试、Core Data 的某些高级特性、SwiftUI 尚未覆盖的控件)你迟早会碰到。这张表的意思是:作为零基础新手,把有限的时间投在右列,左列等真正遇到时再学。

新手不必先学优先学这个
Objective-CSwift
UIKit / AppKitSwiftUI
Core DataSwiftData
ObservableObject / @Published / @StateObject@Observable + @State
NavigationViewNavigationStack / NavigationSplitView
SceneKitRealityKit
StoreKit 1(收据验证那一套)StoreKit 2
XCTest(单元测试场景)Swift Testing(UI 测试仍用 XCUITest)
CocoaPods / CarthageSwift Package Manager
DispatchQueue / 回调地狱async/await + Actor
On-Demand Resources(iOS 27 起已废弃)Apple-Hosted Background Assets
WatchKit 界面层SwiftUI for watchOS
Storyboard / XIBSwiftUI(含 #Preview
MVVM 教条先用 @State,需要时再抽 @Observable

上表中只有 On-Demand Resources 是 Apple 明确标记为 deprecated 的;NavigationView、SceneKit 属于已废弃或明确让位;其余是「有更好的新方案」而非「旧的不能用」。

常见的新手陷阱

  1. 在 `body` 里做副作用。发网络请求、写数据库要放在 .task {} 里。
  2. 滥用 `GeometryReader`。它会占满父容器给的空间,经常导致布局崩坏。先试 ViewThatFits.containerRelativeFrameGrid
  3. 强制解包 `!`。上线后的崩溃报告里一半是这个。
  4. 忽略 `Sendable` 警告,用 @unchecked 硬压。那些警告在保护你。
  5. 一开始就搭复杂架构。独立开发者的最大优势是速度,别自己把它抵消掉。
  6. 自己重写系统组件。系统的分享面板、订阅页、照片选择器已经很好了,而且会随系统更新自动进化。
  7. 不做本地化。中文 App 加一个英文版,潜在市场大好几倍,成本却很低(String Catalog + 一次翻译)。
  8. 发布前不测升级路径。老版本用户升上来数据丢了,是最难挽回的错误。

附录 C. 学习资源与保持更新

官方(优先级最高)

社区

保持更新的节奏

  • 6 月:WWDC。看 Keynote + Platforms State of the Union 建立全局认识,然后按你的技术栈挑 10-15 个 session 看。Guides 页面按主题整理好了,别乱翻。
  • 平时:订阅 Swift.org 博客的月度 "What's new in Swift",以及一两个社区周报。
  • 每次系统大版本:读一遍你用到的框架的 Release Notes,特别注意 deprecation。

最后一句

框架会变,语言会变,设计语言每几年翻新一次。不变的是:理解用户在什么情境下需要什么,然后用最少的东西把它做好。

这份指南给你的是地图。路要自己走,而且必须靠做东西来走——读十篇教程不如做完一个能用的小 App。

祝顺利。


文档基于 2026 年 7 月的公开信息编写,对齐 WWDC26 发布内容。

两点提醒:(1)写作时 iOS 27 / macOS 27、Xcode 27、Swift 6.4、SF Symbols 8 均为 beta,正式版秋季发布,API 仍可能变动;(2)带「今秋」「今年晚些」标注的 App Store 能力尚未完全可用。任何要落到代码或商业决策上的细节,请以 [Apple 官方文档](https://developer.apple.com/documentation/)、[Xcode 系统需求页](https://developer.apple.com/xcode/system-requirements/)和 App Store Connect 的实际状态为准。