当 Android 手机『强行兼容』AirDrop - 肘子的 Swift 周报 #113
AirDrop 让使用者可以在各种不同类似的苹果设备上高效、无损的传输数据,它一直是苹果生态的专属且核心功能。但,这种情况现在出现了“奇怪”的变化。几天前,谷歌宣布在 Pixel 10 中,在没有苹果的参与下,为 Quick Share 提供了 AirDrop 的兼容机制,实现了安卓手机与苹果手机基于 AirDrop 的无线互通。
AirDrop 让使用者可以在各种不同类似的苹果设备上高效、无损的传输数据,它一直是苹果生态的专属且核心功能。但,这种情况现在出现了“奇怪”的变化。几天前,谷歌宣布在 Pixel 10 中,在没有苹果的参与下,为 Quick Share 提供了 AirDrop 的兼容机制,实现了安卓手机与苹果手机基于 AirDrop 的无线互通。
自 iOS 17 起,SwiftUI 引入了 全新的 Observation 模型。
它用三个核心工具彻底重塑了数据管理方式:
@Observable —— 定义可观察的状态模型
@State —— 持有模型实例,等价于旧时代的 @StateObject
@Bindable —— 在视图中实现对 Observable 模型的双向绑定
如果你还在用 ObservableObject、@Published、@StateObject、@ObservedObject、@EnvironmentObject,是时候升级了:新范式更简单、更 Swift、更高性能。
本文将系统梳理 SwiftUI 最新的数据管理体系。
iOS 16 及以前,我们管理状态基本依赖:
ObservableObject
@Published
@StateObject
@ObservedObject
@EnvironmentObject
这些机制的问题:
装饰器太多,容易混乱
生命周期容易搞错(尤其是 @StateObject vs @ObservedObject)
@Published 对属性执行全局广播,性能不够优雅
环境写法不够类型安全
新模型的目标:让 SwiftUI 更简单、更自动、更智能。
新系统中的任何可观察模型,只要声明:
@Observable
class UserModel {
var name = "HanQiu"
var age = 23
}
不再需要:
ObservableObject
@Published
手动发布变更
所有存储属性都是可观察的,SwiftUI 会精确追踪变化来源。
在旧时代,创建页面级别持久的模型需要:
@StateObject var vm = UserModel()
在新系统中:
@State var vm = UserModel()
是的, @State 自动完成以前 @StateObject 的作用:
保持引用类型实例生命周期
在视图重建中保持稳定
触发视图刷新
只要你的模型是 @Observable 的,就可以用 @State 持有。
旧写法(子视图):
struct ProfileView: View {
@ObservedObject var vm: UserModel
}
新写法:
struct ProfileView: View {
var vm: UserModel
}
SwiftUI 会自动观察视图中“被使用的属性”。
你不需要告诉它“这个对象可观察”,它本身就知道(因为模型是 @Observable)。
旧写法:
@EnvironmentObject var settings: SettingsModel
新写法更强、更明确:
struct AppRoot: View {
@State var settings = SettingsModel()
var body: some View {
MainView()
.environment(settings)
}
}
@Environment(SettingsModel.self) var settings
减少误用,也更符合 Swift 语言本身的表达。
@Observable 模型虽然自动可观察,但 UI 控件(如 TextField)需要 双向绑定:
TextField("Name", text: $vm.name)
新模型中,属性只是普通 stored property,不是 Published,不具备 Binding 能力。
于是 Swift 引入:
为 View 提供 绑定视角的模型访问。
@Observable
class UserModel {
var name = ""
var age = 18
}
struct EditUserView: View {
@Bindable var user: UserModel
var body: some View {
Form {
TextField("Name", text: $user.name)
Stepper("Age: \(user.age)", value: $user.age)
}
}
}
只需标记 @Bindable,模型属性即可自动得到 $binding。
是否需要取决于:
| 情况 | 是否需要 @Bindable |
|---|---|
| 仅用于展示,不会修改模型 | ❌ No |
| 需要用 TextField / Toggle / Stepper 修改模型 | ✔ Yes |
| 子视图要修改父模型 | ✔ Yes |
| 完全只读视图 | ❌ No |
越“表单”风格的页面,越需要 @Bindable。
你也可以只在 body 内使用 Bindable:
var body: some View {
@Bindable var b = user // 局部绑定
VStack {
TextField("Name", text: $b.name)
Stepper("Age: \(b.age)", value: $b.age)
}
}
不会污染结构体属性定义,适合仅局部可编辑的 UI。
@Observable —— 使模型可观察
@State —— 在 View 中持有模型(生命周期 = 旧 @StateObject)
@Bindable —— 提供绑定能力,允许 UI 修改模型
一个“完整数据流”的表达式:
@Observable 定义状态 → @State 持有 → @Bindable 编辑 → SwiftUI 自动刷新
@Observable
class ProfileModel {
var name = "HanQiu"
var level = 1
}
struct ProfileView: View {
@State var profile = ProfileModel()
var body: some View {
VStack {
Text("Name: \(profile.name)")
Text("Level: \(profile.level)")
EditSection(profile: profile)
}
}
}
struct EditSection: View {
@Bindable var profile: ProfileModel
var body: some View {
VStack {
TextField("Name", text: $profile.name)
Stepper("Level: \(profile.level)", value: $profile.level)
}
.padding()
}
}
无需 @Published,不用 @StateObject,不需要 @ObservedObject。
SwiftUI 的数据管理彻底简化。
| 旧 API | 新 API |
|---|---|
| ObservableObject | @Observable |
| @Published | 不需要 |
| @StateObject | @State |
| @ObservedObject | 删除,直接传模型 |
| @EnvironmentObject | .environment(model) + @Environment(Model.self) |
| 双向绑定属性 | 使用 @Bindable |
SwiftUI 从 iOS17 开始进入 Observation 时代:
@Observable → 自动观察
@State → 管理模型生命周期
@Bindable → 构建表单/编辑 UI 的关键
更少的装饰器
更精准的性能优化
更符合 Swift 语言设计哲学
如果你写 SwiftUI,这套新范式未来几年都会是主流。