与此项目相关的文章
SwiftUI + Combine的清洁架构
这是一个演示项目,展示了使用清洁架构搭建SwiftUI应用程序的设置。
该应用使用restcountries.com的REST API来显示国家列表及其详细信息。
查看mvvm分支以获取同一应用的MVVM修订版。
关于如何在应用中处理认证状态的示例,你可以参考我的另一个小项目,它利用锁和键的原理来解决这个问题。
主要特点
- 原生SwiftUI + Combine实现
- 解耦的表现层、业务逻辑层和数据访问层
- 完整的测试覆盖率,包括UI(感谢ViewInspector)
- Redux风格的集中式
AppState
作为单一真实来源 - 使用CoreData进行数据持久化
- 原生SwiftUI依赖注入
- 程序化导航。带有深链接的推送通知
- 基于泛型构建的简单而灵活的网络层
- 处理系统事件(如
didBecomeActive
、willResignActive
) - 遵循SOLID、DRY、KISS、YAGNI原则设计
- 为可扩展性而设计。可作为构建大型生产应用的参考
架构概览
表现层
不包含业务逻辑的SwiftUI视图,它们是状态的函数。
副作用由用户操作(如按钮点击)或视图生命周期事件onAppear
触发,并转发给Interactors
。
状态和业务逻辑层(AppState
+ Interactors
)通过@Environment
原生注入到视图层次结构中。
业务逻辑层
业务逻辑层由Interactors
表示。
Interactors接收执行工作的请求,如从外部源获取数据或进行计算,但它们从不直接返回数据。
相反,它们将结果转发给AppState
或Binding
。后者用于当工作结果(数据)仅被一个视图本地使用且不属于AppState
时。
之前,此应用没有使用CoreData进行持久化,所有加载的数据都存储在AppState
中。
有了持久化层,我们有两个选择 - 要么将数据库内容加载到AppState
中,要么通过Binding
按需从Interactors
提供数据。
当你没有大量数据时,第一个选项最合适,例如,当你只是在UserDefaults
中存储最后使用的登录邮箱时。然后,相应的字符串值可以在启动时加载到AppState
中,并在用户更改输入时由Interactor
更新。
当你有大量数据并引入功能齐全的数据库进行本地存储时,第二个选项更好。
数据访问层
数据访问层由Repositories
表示。
Repositories提供异步API(来自Combine的Publisher
)用于在后端或本地数据库上执行CRUD操作。它们不包含业务逻辑,也不会改变AppState
。Repositories只能由Interactors访问和使用。