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

相关推荐
文火冰糖的硅基工坊3 小时前
《投资-93》价值投资者的认知升级与交易规则重构 - 衡量公司价值的本质是在公司的整个存续期间能够创造多少自由现金流,而不是当下有多少现金流。
重构·架构·产业链
方君宇5 小时前
iOS App小组件(Widget)设置透明背景
ios
jh_cao5 小时前
(3)容器布局进阶:Spacer、Divider、Frame 与 Alignment
swiftui
恋猫de小郭5 小时前
React 和 React Native 不再直接归属 Meta,React 基金会成立
android·前端·ios
brzhang5 小时前
当我第一次看到 snapDOM,我想:这玩意儿终于能解决网页「截图」这破事了?
前端·后端·架构
东坡肘子6 小时前
Sora 2:好模型,但未必是好生意 | 肘子的 Swift 周报 #0105
人工智能·swiftui·swift
jh_cao16 小时前
(1)SwiftUI 的哲学:声明式 UI vs 命令式 UI
ui·swiftui·命令模式
可触的未来,发芽的智生17 小时前
触摸未来2025.10.06:声之密语从生理构造到神经网络的声音智能革命
人工智能·python·神经网络·机器学习·架构
HarderCoder17 小时前
Swift 6 并发深渊:@unchecked Sendable 与“隐式 MainActor”如何合谋杀死你的 App
swiftui·swift