RxSwift学习之常用框架、MVVM
函数响应式编程 清晰简洁、易读、易维护的代码,下面我们看下 RxSwift 的最佳搭档:MVVM。
函数响应式编程可见另外两篇文章: RxSwift学习:函数响应式编程思想、 RxSwift学习:函数响应式编程简介
资料下载:下载地址
MVVM
原先常用的架构:MVC
Model:数据层。负责读写数据,保存 App 状态等
Controller:业务逻辑层。负责业务逻辑、事件响应、数据加工等工作
缺点:
ViewController 既扮演了 View 的角色,又扮演了 ViewController 的角色
而 Model 在 VIewController 中又可以直接与 View 进行交互
当 App 交互复杂的时候,就会发现 ViewController 将变得十分臃肿,大量代码被添加到控制器中,使得控制器负担过重。
MVVM
优点:
可以对 ViewController 进行瘦身
实现逻辑视图的复用。比如一个 ViewModel 可以绑定到不同的 View 上,让多个 View 重用相同的视图逻辑。
而且使用 MVVM 可以大大降低代码的耦合性,方便进行单元测试以及维护,也方便多人协作开发(比如一个人负责逻辑实现,一个人负责 UI 实现)。
缺点:
相较于 MVC,使用 MVVM 会轻微的增加代码量,但是总体上减少了代码的复杂性。
还有就是有一定的学习成本(如何数据绑定等)。
MVVM
MVVM 是 Model-View-ViewModel 的简写。如果你已经对 MVC 非常熟悉了,那么上手 MVVM 也是非常容易的。
MVC 是 Model-View-Controller 的简写。MVC 主要有三层:
Model 数据层,读写数据,保存 App 状态
View 页面层,和用户交互,向用户显示页面,反馈用户行为
ViewController 逻辑层,更新数据,或者页面,处理业务逻辑
MVC 可以帮助你很好的将数据,页面,逻辑的代码分离开来。使得每一层相对独立。这样你就能够将一些可复用的功能抽离出来,化繁为简。只不过,一旦 App 的交互变复杂,你就会发现 ViewController 将变得十分臃肿。大量代码被添加到控制器中,使得控制器负担过重。此时,你就需要想办法将控制器里面的代码进一步地分离出来,对 APP 进行重新分层。而 MVVM 就是一种进阶的分层方案。
MVVM
MVVM 和 MVC 十分相识。只不过他的分层更加详细:
Model 数据层,读写数据,保存 App 状态
View 页面层,提供用户输入行为,并且显示输出状态
ViewModel 逻辑层,它将用户输入行为,转换成输出状态
ViewController 主要负责数据绑定
没错,ViewModel 现在是逻辑层,而控制器只需要负责数据绑定。如此一来控制器的负担就减轻了许多。并且 ViewModel 与控制器以及页面相独立。那么,你就可以跨平台使用它。你也可以很容易地测试它。
示例
这里我们将用 MVVM 来重构输入验证。
重构前:
class SimpleValidationViewController : ViewController { ... override func viewDidLoad() { super.viewDidLoad() ... let usernameValid = usernameOutlet.rx.text.orEmpty .map { $0.characters.count >= minimalUsernameLength } .share(replay: 1) let passwordValid = passwordOutlet.rx.text.orEmpty .map { $0.characters.count >= minimalPasswordLength } .share(replay: 1) let everythingValid = Observable.combineLatest( usernameValid, passwordValid ) { $0 && $1 } .share(replay: 1) usernameValid .bind(to: passwordOutlet.rx.isEnabled) .disposed(by: disposeBag) usernameValid .bind(to: usernameValidOutlet.rx.isHidden) .disposed(by: disposeBag) passwordValid .bind(to: passwordValidOutlet.rx.isHidden) .disposed(by: disposeBag) everythingValid .bind(to: doSomethingOutlet.rx.isEnabled) .disposed(by: disposeBag) doSomethingOutlet.rx.tap .subscribe(onNext: { [weak self] in self?.showAlert() }) .disposed(by: disposeBag) } ... }复制代码
ViewModel
ViewModel 将用户输入行为,转换成输出的状态:
class SimpleValidationViewModel { // 输出 let usernameValid: Observable<Bool> let passwordValid: Observable<Bool> let everythingValid: Observable<Bool> // 输入 -> 输出 init( username: Observable<String>, password: Observable<String> ) { usernameValid = username .map { $0.characters.count >= minimalUsernameLength } .share(replay: 1) passwordValid = password .map { $0.characters.count >= minimalPasswordLength } .share(replay: 1) everythingValid = Observable.combineLatest(usernameValid, passwordValid) { $0 && $1 } .share(replay: 1) } }复制代码
输入:
username 输入的用户名
password 输入的密码
输出:
usernameValid 用户名是否有效
passwordValid 密码是否有效
everythingValid 所有输入是否有效
在 init
方法内部,将输入转换为输出。
ViewController
ViewController 主要负责数据绑定:
class SimpleValidationViewController : ViewController { ... private var viewModel: SimpleValidationViewModel! override func viewDidLoad() { super.viewDidLoad() ... viewModel = SimpleValidationViewModel( username: usernameOutlet.rx.text.orEmpty.asObservable(), password: passwordOutlet.rx.text.orEmpty.asObservable() ) viewModel.usernameValid .bind(to: passwordOutlet.rx.isEnabled) .disposed(by: disposeBag) viewModel.usernameValid .bind(to: usernameValidOutlet.rx.isHidden) .disposed(by: disposeBag) viewModel.passwordValid .bind(to: passwordValidOutlet.rx.isHidden) .disposed(by: disposeBag) viewModel.everythingValid .bind(to: doSomethingOutlet.rx.isEnabled) .disposed(by: disposeBag) doSomethingOutlet.rx.tap .subscribe(onNext: { [weak self] in self?.showAlert() }) .disposed(by: disposeBag) } ... }复制代码
输入:
username 将输入的用户名传入 ViewModel
password 将输入的密码传入 ViewModel
输出:
usernameValid 用用户名是否有效,来控制提示语是否隐藏,密码输入框是否可用
passwordValid 用密码是否有效,来控制提示语是否隐藏
everythingValid 用两者是否同时有效,来控制按钮是否可点击
当 App 的交互变复杂时,你仍然可以保持控制器结构清晰。这样可以大大的提升代码可读性。将来代码维护起来也就会容易许多了。
作者:iOS沐橙君
链接:https://juejin.cn/post/7023288275041255431