ReactiveCocoaSwift开发认知

Swift3+MVVM+ReactiveCocoa5实战1

2017-05-01  本文已影响722人  蜜蜂6520

关于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资料

如果觉得我的文章对您有用,请点击喜欢。您的支持将鼓励我继续创作!

这是作者每一次写帖子,大家有什么不懂或我哪里写错了都可以评论留言,我一定会回复的~

上一篇下一篇

猜你喜欢

热点阅读