iOS 社区有一场持续了多年的热门争论。
“SwiftUI 需要 ViewModel 吗?”
上一篇我们总结过,MVVM 的核心是绑定,而 SwiftUI 已将绑定内置在框架中。于是有人开始认为,ViewModel 这一层本身就是重复的。这就是所谓的 MV(Model-View)模式阵营。
作为本系列的最后一篇,今天我们会公平分析双方的论据,并总结实际可以采用哪些判断标准。
SwiftUI 的 View 本来就很像 ViewModel
回顾 UIKit 为什么需要 MVVM,原因如下。
- UIViewController 变得臃肿,因此需要把状态和逻辑移到外部
- 需要绑定来同步状态和界面
但 SwiftUI 的 View 本来就是状态的函数。body它只接收状态并返回界面声明,@State值发生变化时,框架会自动重新绘制。不需要为了绑定编写 Combine 订阅代码。
此外,SwiftUI 的 View 不是类,而是值类型 struct。它不像 UIViewController 那样是可能膨胀到数千行的生命周期大对象,而更像每次渲染都会重新创建的轻量设计图。
MV 阵营的观点由此出发:既然 View 不会膨胀,绑定又是免费的,那么为每个界面创建 ViewModel 类,难道不是把 UIKit 的习惯惯性地带过来吗?
MV 阵营的论据
在 MV 模式中,不为每个界面设置 ViewModel,而是由 View 直接使用状态工具。
- 只属于一个 View 的状态放在
@State这里 - 多个界面共享的状态作为
@Observable模型@Environment注入 - 服务器数据由 Model 层的 store 或 client 负责
struct ProfileView: View {
@Environment(UserStore.self) private var store
@State private var isEditing = false
var body: some View {
List(store.posts) { PostRow(post: $0) }
.task { await store.loadPosts() }
}
}
Apple 的官方示例代码(Fruta、Food Truck 等)大体也采用这种结构。多个 View 共享一个领域级@Observable模型,而不是每个界面单独使用 ViewModel。
这种方式的优点很明确:省去了每个界面创建一个类的样板代码,也可以直接使用@State和@Binding等框架工具。
MVVM 阵营的反驳
另一方的论据也不容小觑。
**第一,逻辑会渗入 View。**当“如果帖子数为 0、加载已结束且没有错误,就显示提示文字”之类的条件开始以三元运算符堆积在body中时,即使是值类型,也同样难以阅读。
**第二,测试问题。**View 的body很难通过单元测试执行。如果展示逻辑位于 View 内,它也会一起进入测试盲区。将其提取到 ViewModel 后,就会变成可以直接验证的纯 Swift 对象。
**第三,规模问题。**如果应用有几十个界面并由多人协作,就需要一套关于状态和逻辑位置的一致规则。为每个界面提供一个叫作 ViewModel 的固定位置,有助于降低协作成本。
不是名称,而是依赖方向
综合双方论据后可以发现,这场争论实际上讨论的是逻辑和状态应该放在哪里,而不是是否存在一个名为 ViewModel 的类。以此作为判断标准,大多数情况都能理清。
- 只属于一个 View 的 UI 状态(切换开关、焦点、是否显示工作表)应
@State放在 View 中。把它提取到 ViewModel 是过度设计。 - 多个界面共享的领域状态应创建为
@Observable模型并注入。无论叫 Store 还是 ViewModel,本质都一样。 - 当界面专用的转换逻辑变得复杂时(格式化、显示条件、排序和筛选),就将它提取到 View 外部的可测试类型中。这实际上就是 ViewModel。
- 无论哪一种方式,只要遵守View 不直接访问网络或数据库这一依赖方向,MV 和 MVVM 基本只是名称不同。
归根结底,“SwiftUI 不需要 ViewModel”可以这样概括:不必机械地为每个界面创建 ViewModel,但这不代表应该移出 View 的逻辑就消失了。
系列总结
三篇 iOS 架构系列可以按主题各用一句话概括如下。
- MVC:UIViewController 同时承担 View 和 Controller 的职责,因此责任集中。对于小型界面仍然有效。
- MVVM:将界面状态移到 ViewModel 中,并通过绑定同步。没有绑定就只完成了一半。
- MV 争论:SwiftUI 内置了绑定,因此没有理由强制每个界面都使用 ViewModel。不过,逻辑的位置和依赖方向仍然需要设计。
架构不是潮流,而是适合团队和应用规模的权衡选择。如果反响不错,之后还会继续介绍 VIPER 和 TCA 等更重量级的选项。

![[iOS 架构 #3] SwiftUI 不需要 ViewModel?MV 模式争论总结 封面图](/assets/images/posts/7bedf164-d196-4a13-815a-c43b2968f9f4/1.jpg)