(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。

相关推荐
Psycho_MrZhang2 小时前
多 Agent 研究系统的架构与实践
人工智能·架构
时空节拍AI数字人2 小时前
数字文旅补贴来了,景区申报要注意什么?
大数据·人工智能·百度·3d·ai·架构·aigc
Dawson Zhu2 小时前
《Agentic Design Patterns》第 8 章导读:记忆管理(Memory Management)
人工智能·语言模型·架构·aigc·agi
code_slave(码畜)3 小时前
微服务架构落地:消息队列架构设计(下篇)——消息堆积、死信治理与集群监控告警实战
spring boot·spring cloud·微服务·云原生·中间件·架构
茶底世界之下3 小时前
预乘 Alpha 图层合成为什么会越叠越暗?
架构·swift
蜡台3 小时前
AI Agent 架构终极教程:从原理分层、四大范式到生产级落地实战
网络·人工智能·架构
用户8314550980315 小时前
如一 Agent 架构解读(二):上下文工程——为模型构造它唯一的现实
人工智能·架构
怕浪猫6 小时前
Agent 工程化面试:从开发到部署的 5 个关键问题
面试·架构·github
行者全栈架构师6 小时前
【鸿蒙心迹】鸿蒙网络请求架构实战——@ohos.net.http 到 Axios 封装、拦截器与统一错误处理(HarmonyOS 7.x)
前端·算法·架构
m0_587383006 小时前
工业场景设备维修维护实战技巧 全流程标准化落地与常见问题排查指南
java·spring·小程序·架构·需求分析