(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:任务数据结构

swift 复制代码
struct Task: Identifiable, Codable {
    let id = UUID()
    var title: String
    var isDone: Bool = false
}

⚙️ ViewModel:任务逻辑与状态管理

swift 复制代码
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:声明式界面展示

swift 复制代码
struct TaskListView: View {
    @StateObject private var viewModel = TaskViewModel()
    @State private var newTask = ""
    
    var body: some View {
        NavigationView {
            VStack {
                List {
                    ForEach(viewModel.tasks) { task in
                        HStack {
                            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 框架或异步函数。

swift 复制代码
@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。

相关推荐
晓py3 小时前
全面认识 InnoDB:从架构到 Buffer Pool 深入解析
mysql·架构
Hooomeey3 小时前
深度解析线程与线程池:从 OS 调度内核到 Java 并发架构的演进逻辑
java·后端·架构
吃饺子不吃馅4 小时前
Web端PPT应用画布方案:Canvas 还是 DOM?
前端·架构·canvas
加油乐5 小时前
解决 iOS 端输入框聚焦时页面上移问题
前端·javascript·ios
老纪的技术唠嗑局6 小时前
分库分表MyCat 架构迁移 OceanBase | 百丽核心财务系统迁移经验总结与问题汇总
数据库·架构·oceanbase
电话交换机IPPBX-3CX10 小时前
电话交换机软件 3CX iOS 应用 V5.4 Beta 更新
ios·软件更新·ip pbx·电话交换机
WAsbry14 小时前
NFC开发系列专栏 - 第三篇:无界面NFC后台服务方案
android·程序员·架构
WAsbry14 小时前
NFC开发系列 - 第二篇:NFC企业级架构设计与最佳实践
android·程序员·架构
短视频矩阵源码定制14 小时前
矩阵系统源码推荐:技术架构与功能完备性深度解析
java·人工智能·矩阵·架构
工藤学编程16 小时前
深入Rust:Tokio多线程调度架构的原理、实践与性能优化
性能优化·架构·rust