@[TOC](iOS架构篇-4 架构模式MVVM)
MVVM原理

MVVM(Model–View–Viewmodel)是一种软件架构模式。
View:页面UI、动画、控件、VC层,通常有UI控件、UI事件暴露出来
ViewModel:业务数据层,通常为View层持有,接受View层事件,绑定View层控件
Model:数据模型处理层,通常是网络接口请求,本地数据处理
MVVM 登录例子
下面以RXSwift框架结合登录功能具体说明整个流程:

View:
登录界面,账号 accountTxtField: UITextField、密码passwordTxtField: UITextField、消息提示msgLbl: UILabel、登录按钮loginBtn: UIButton
//
// LoginVC.swift
// ProjectApp
//
// Created by jack on 2021/1/11.
//
import UIKit
class LoginVC: BaseVC {
@IBOutlet weak var accountTxtField: UITextField!
@IBOutlet weak var passwordTxtField: UITextField!
@IBOutlet weak var loginBtn: UIButton!
@IBOutlet weak var msgLbl: UILabel!
var vm: LoginVM?
let msgObservable: PublishSubject<String> = PublishSubject<String>()
override func viewDidLoad() {
super.viewDidLoad()
self.title = "登录"
// 传入账号、密码、登录按钮的事件监听
self.vm = LoginVM(input: (account: accountTxtField.rx.text.orEmpty.asObservable(), password: passwordTxtField.rx.text.orEmpty.asObservable(), loginTap: loginBtn.rx.tap.asObservable()))
// 登录按钮订阅ViewModel的signupEnabled属性,实时监听输入的账号、密码是否符合要求来判断登录按钮能否点击
self.vm?.signupEnabled.subscribe(onNext: { [weak self](valid) in
guard let self = self else { return }
self.loginBtn.isEnabled = valid
self.loginBtn.alpha = valid ? 1.0 : 0.5
}, onError: { (error) in
}, onCompleted: {
}, onDisposed: {
}).disposed(by: disposeBag)
// 订阅ViewModel的loginResult属性,实时监听登录结果
self.vm?.loginResult.subscribe(onNext: { [weak self](suc, errorMsg) in
guard let self = self else { return }
if suc { // 登录成功
DPrint(message: "登录成功")
self.msgObservable.onNext("登录成功")
} else {// 账号/密码错误
DPrint(message: "登录失败")
self.msgObservable.onNext(errorMsg)
}
}, onError: { (e: Error) in
// 网络出错、接口返回数据模型不对等异常情况回调
DPrint(message: "登录失败:\(e.localizedDescription)")
}, onCompleted: {
}, onDisposed: {
}).disposed(by: disposeBag)
// 消息提示UILabel绑定
self.msgObservable.bind(to: self.msgLbl.rx.text).disposed(by: disposeBag)
}
}
ViewModel:
1.本地验证账号、密码
2.监听登录按钮
3.登录接口反馈
//
// LoginVM.swift
// ProjectApp
//
// Created by jack on 2021/1/11.
//
import Foundation
class LoginVM {
let validateAccount: Observable<Bool>
let validatePassword: Observable<Bool>
let signupEnabled: Observable<Bool>
let loginResult: Observable<(Bool, String)>
init(input:(
account: Observable<String>,
password: Observable<String>,
loginTap: Observable<Void>
)) {
/// 验证账号
validateAccount = input.account.flatMap({ (account) -> Observable<Bool> in
var flag = true
if account.count < 5 {
flag = false
}
return Observable.of(flag)
})
/// 验证密码
validatePassword = input.password.flatMap({ (password) -> Observable<Bool> in
var flag = true
if password.count < 5 {
flag = false
}
return Observable.of(flag)
})
/// 有效账号&有效密码则可以点击登录按钮
signupEnabled = Observable.combineLatest(validateAccount, validatePassword){
accountFlag, passwordFlag in
accountFlag && passwordFlag
}.distinctUntilChanged()
.share(replay: 1)
/// 合并账号和密码,组成元组
let accountAndPassword = Observable.combineLatest(input.account, input.password) { (account: $0, password: $1) }
// 登录按钮点击事件
self.loginResult = input.loginTap.withLatestFrom(accountAndPassword).flatMapLatest { (pair) -> Observable<RespEntity<User>> in
let observable: Observable<RespEntity<User>> = rxrequest(params: .Login(account: pair.account, password: pair.password), errorToast: true, hud: true)
return observable
}
/// 登录接口返回数据进行Observable转换
.flatMap({ (resp: RespEntity<User>) -> Observable<(Bool, String)> in
if let _ = resp.data { // 返回有用户数据则判断为登录成功
// TODO 保存用户信息
return Observable.of((true, ""))
} else {
var msg = "网络信号差,请稍后重试"
if let str = resp.msg { // 如果返回有提示信息(如:账号错误、密码错误等)则提示返回的信息
msg = str
}
return Observable.of((false, msg))
}
})
}
}
Model:
请求登录接口
网友评论