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

相关推荐
未来侦察班1 天前
一晃13年过去了,苹果的Airdrop依然很坚挺。
macos·ios·苹果vision pro
yunteng5211 天前
通用架构(同城双活)(单点接入)
架构·同城双活·单点接入
麦聪聊数据1 天前
Web 原生架构如何重塑企业级数据库协作流?
数据库·sql·低代码·架构
程序员侠客行1 天前
Mybatis连接池实现及池化模式
java·后端·架构·mybatis
bobuddy1 天前
射频收发机架构简介
架构·射频工程
桌面运维家1 天前
vDisk考试环境IO性能怎么优化?VOI架构实战指南
架构
一个骇客1 天前
让你的数据成为“操作日志”和“模型饲料”:事件溯源、CQRS与DataFrame漫谈
架构
锐意无限1 天前
Swift 扩展归纳--- UIView
开发语言·ios·swift
符哥20081 天前
用Apollo + RxSwift + RxCocoa搭建一套网络请求框架
网络·ios·rxswift
鹏北海-RemHusband1 天前
从零到一:基于 micro-app 的企业级微前端模板完整实现指南
前端·微服务·架构