(3)SwiftUI 的状态之上:数据流与架构(MVVM in SwiftUI)
🏗️ SwiftUI 的状态之上:数据流与架构(MVVM in SwiftUI)
“在状态之上,组织思想;在数据之上,构建架构。”
一、从状态到架构:为什么需要 MVVM
在前两篇中,我们认识了 SwiftUI 的核心哲学:
- 声明式 UI —— 描述“我想要什么”;
- 状态驱动 —— “UI = f(State)”。
这些理念在小型页面中非常高效。
但当项目复杂起来,比如多页跳转、网络请求、权限控制时,
你会发现仅靠 @State
和 @ObservedObject
还不够。
这时候,我们需要更高层次的组织方式 —— MVVM 架构。
二、什么是 MVVM?
MVVM 是 Model-View-ViewModel 的缩写,是 SwiftUI 官方推荐的架构。
层级 | 作用 | 在 SwiftUI 中的体现 |
---|---|---|
Model | 数据层(结构体/类) | 普通数据类型、网络返回结果 |
ViewModel | 状态管理层,业务逻辑 | ObservableObject + @Published |
View | 视图层(UI) | SwiftUI View 结构体 |
三、为什么 SwiftUI 天然适合 MVVM?
UIKit 时代,ViewController 又管 UI 又管逻辑,是“大杂烩”。
SwiftUI 把 UI 从控制器中完全剥离,让 View
成为纯函数式结构。
因此:
- View 只负责展示;
- ViewModel 负责状态和逻辑;
- Model 负责数据。
三者职责分明、天然分层。
四、MVVM 结构图
+----------------------+| View || SwiftUI 描述 UI |+----------+-----------+|@ObservedObject|+----------v-----------+| ViewModel || 管理状态 + 业务逻辑 || ObservableObject |+----------+-----------+|调用网络层|+----------v-----------+| Model || 数据结构 + 请求响应 |+----------------------+
五、从一个 To-Do App 看 MVVM 的落地
我们以一个简单的待办事项应用为例。
🧱 Model:任务数据结构
struct Task: Identifiable, Codable {let id = UUID()var title: Stringvar isDone: Bool = false
}
⚙️ ViewModel:任务逻辑与状态管理
class TaskViewModel: ObservableObject {@Published var tasks: [Task] = []func addTask(title: String) {guard !title.isEmpty else { return }tasks.append(Task(title: title))}func toggleDone(task: Task) {if let index = tasks.firstIndex(where: { $0.id == task.id }) {tasks[index].isDone.toggle()}}func removeTask(at offsets: IndexSet) {tasks.remove(atOffsets: offsets)}
}
🪟 View:声明式界面展示
struct TaskListView: View {@StateObject private var viewModel = TaskViewModel()@State private var newTask = ""var body: some View {NavigationView {VStack {List {ForEach(viewModel.tasks) { task inHStack {Image(systemName: task.isDone ? "checkmark.circle.fill" : "circle").onTapGesture { viewModel.toggleDone(task: task) }Text(task.title).strikethrough(task.isDone)}}.onDelete(perform: viewModel.removeTask)}HStack {TextField("New Task", text: $newTask)Button("Add") {viewModel.addTask(title: newTask)newTask = ""}}.padding()}.navigationTitle("My Tasks")}}
}
六、SwiftUI MVVM 的关键要点
概念 | SwiftUI 实现 | 说明 |
---|---|---|
数据绑定 | @ObservedObject 、@StateObject | View 与 ViewModel 双向绑定 |
自动刷新 | @Published | 状态更新时自动刷新 UI |
单向数据流 | ViewModel → View | 数据从上而下传递 |
无副作用 UI | View 只描述布局,不管理逻辑 | 视图是“结果”,非“命令” |
七、MVVM + SwiftUI 的典型组合
模块 | 说明 | 实现方式 |
---|---|---|
View | 负责界面展示 | struct + body |
ViewModel | 业务逻辑 + 状态 | ObservableObject |
Model | 数据结构 | struct |
网络层 | 数据请求 | async/await + URLSession |
存储层 | 本地数据 | UserDefaults / CoreData |
全局状态 | 环境对象 | @EnvironmentObject |
八、实际项目中的 MVVM 分层建议
当你项目越来越复杂,可以这样组织目录:
📁 ProjectName┣ 📂 Models┃ ┗ Task.swift┣ 📂 ViewModels┃ ┗ TaskViewModel.swift┣ 📂 Views┃ ┗ TaskListView.swift┣ 📂 Services┃ ┗ NetworkManager.swift┣ 📂 Resources┃ ┗ Assets.xcassets┗ ProjectNameApp.swift
九、再往上一步:MVVM + Combine + async/await
在大型项目中,ViewModel 还经常结合 Combine 框架或异步函数。
@MainActor
class NewsViewModel: ObservableObject {@Published var articles: [Article] = []func fetchNews() async {do {let url = URL(string: "https://newsapi.org/...")!let (data, _) = try await URLSession.shared.data(from: url)let decoded = try JSONDecoder().decode([Article].self, from: data)articles = decoded} catch {print("Error:", error)}}
}
SwiftUI 与
async/await
完美融合,
让“声明式 UI + 异步状态”成为一种自然写法。
十、结语:SwiftUI 架构的三重境界
1️⃣ 声明式 UI
“我描述界面,不命令界面。”
2️⃣ 状态驱动
“UI = f(State)。”
3️⃣ 架构思维(MVVM)
“状态有归属,逻辑有层次。”
当你理解这三层关系时,
SwiftUI 就不再只是语法糖,
而是一个完整、优雅、可扩展的 UI 哲学体系。
从命令到声明,从变量到状态,从函数到架构。
这就是 SwiftUI。