Swift3+MVVM+ReactiveCocoa5实战1
关于Swift3
14年苹果公司推出Swift语言,过程中语法不间断地变,后来Swift3大版本出来,语法稳定了很多。作者是从Swift3开始学习的,虽然可以继续使用oc,但作者认为Swift语言类型安全、语法优雅、集成了很多其他语言的优点、效率高,而且是苹果公司主推的开发语言,Apple Watch等只能用Swift语言开发,现在网上很多Demo基于Swift,不会怎么行。所以iOS程序员要与时俱进,Swift现在是必备技能,多对照学习下oc和Swift的不同写法就能互相转化。
关于MVVM
MVVM是iOS架构的一种。当然还有其它,比如我们经常使用的MVC和MVP。MVC有一个缺点就是时间长Controller会过于臃肿,走读困难、重构困难、单元测试困难,当然可以优化MVC。这里我们使用的替换架构是MVVM。
M:模型(包括数据Model和请求Model)
V:视图(包括Controller和View)
VM:视图模型(Controller中的业务逻辑)
可以实现高内聚低耦合。
关于ReactiveCocoa5
ReactiveCocoa5以下简称RAC5
要使用MVVM,可以自己使用Block,KVO,代理、通知的方式实现数据绑定,但与RAC5配合使用会方便很多。现在网上的RAC资料大多是基于RAC2.5 基于oc的,但是官方RAC已经更新到了RAC5 基于Swift。作者当然不愿意使用旧版本的,所以先从RAC2.5学习理论用法,再从RAC4中寻找Swift的使用方式,再结合官方github的英文文档,实现了简单的登录界面Demo分享给大家。
废话不多说。
Talk is cheap. Show me the code
需求描述:
登录界面,帐号和密码文本框只有它们都有值时,登录按钮启用,否则禁用置灰。
点击登录按钮,成功后打印登录成功
Model
class LoginDataModel: NSObject {
var account:String = ""//帐号
var pwd:String = ""//密码
}
ViewController
//这里插一句,导入RAC5,使用cocoapods
/*
platform :ios, '8.0'
target 'ReactiveCocoaDemo' do
use_frameworks!
pod 'ReactiveCocoa', '~> 5.0.0'
end
*/
// 使用RAC5要导入这3个模块
import ReactiveCocoa
import ReactiveSwift
import Result
class LoginViewController: UIViewController {
lazy var loginViewModel = {
return LoginViewModel()
}()// 延迟加载,每一次使用时初始化
@IBOutlet weak var accountTextField: UITextField!//storyboard的帐号控件
@IBOutlet weak var pwdTextField: UITextField!//storyboard的帐号控件
@IBOutlet weak var loginBtn: UIButton!//storyboard的登录按钮
override func viewDidLoad() {
super.viewDidLoad()
bindModel()
}
func bindModel() {
// 帐号控件内容同步到DataModel的帐号属性
let accountProperty = DynamicProperty<String>(object: loginViewModel.loginDataModel,
keyPath: #keyPath(LoginDataModel.account))
accountProperty <~ accountTextField.reactive.continuousTextValues
// 密码控件内容同步到DataModel的密码属性
let pwdProperty = DynamicProperty<String>(object: loginViewModel.loginDataModel,
keyPath: #keyPath(LoginDataModel.pwd))
pwdProperty <~ pwdTextField.reactive.continuousTextValues
// 信号生成器的值同步到登录按钮的启用状态上
loginBtn.reactive.isEnabled <~ loginViewModel.enableLoginProducer
}
@IBAction func login(_ sender: Any) {
// 点击登录按钮,执行ViewModel的登录业务,观察结果并做出响应
loginViewModel.loginAction.apply(()).start { (event) in
switch event {
case let .value(value):
if value == "登录成功" {
print(value)
}
default:
break
}
}
}
}
ViewModel
import ReactiveCocoa
import ReactiveSwift
import Result
class LoginViewModel: NSObject {
lazy var loginDataModel = {
return LoginDataModel()
}()
var enableLoginProducer:SignalProducer<Bool,NoError>!// 信号生成器,登录按钮启用发送true,禁用发送false
var loginAction:Action<(),String,AnyError>!//登录业务
override init() {
super.init()
initialBind()
}
func initialBind() {
// 监听DataModel帐号属性值变化
let accountProducer = loginDataModel.reactive.values(forKeyPath: #keyPath(LoginDataModel.account))
// 监听DataModel密码属性值变化
let pwdProducer = loginDataModel.reactive.values(forKeyPath: #keyPath(LoginDataModel.pwd))
// 当DataModel帐号和密码属性都不为空时,信号生成器发送true,否则发送false
enableLoginProducer = SignalProducer.combineLatest(accountProducer, pwdProducer).map { (account,pwd) -> Bool in
let accountStr = account as! String?
let pwdStr = pwd as! String?
let enabled = (accountStr != nil && accountStr?.characters.count != 0) && (pwdStr != nil && pwdStr?.characters.count != 0)
return enabled
}
// 登录业务
loginAction = Action<(),String,AnyError> { (_) -> SignalProducer<String, AnyError> in
SignalProducer({ (observer, _) in
observer.send(value: "登录成功")
observer.sendCompleted()
})
}
}
}
以上就是简单Demo的使用,忽略了网络请求,下一篇会有网络请求Demo
RAC5的学习资料
最快让你上手ReactiveCocoa之基础篇
ReactiveCocoa 4 官方文档翻译
官方GitHub资料
如果觉得我的文章对您有用,请点击喜欢。您的支持将鼓励我继续创作!
这是作者每一次写帖子,大家有什么不懂或我哪里写错了都可以评论留言,我一定会回复的~