美文网首页
ReactiveCocoa 学习笔记

ReactiveCocoa 学习笔记

作者: 心甘情愿_Root | 来源:发表于2017-08-25 15:07 被阅读0次

    ReactiveCocoa

    配置安装RAC

    • Carthage 安装

    项目目录下执行

    $ touch Cartfile
    $ vim Cartfile
    

    并直接写入Cartfile

    github "ReactiveCocoa/ReactiveCocoa" ~> 6.0
    

    保存后直接执行,注意我的版本,可能与实际的版本有差别。

    $ carthage update
    *** Cloning ReactiveCocoa
    *** Cloning ReactiveSwift
    *** Cloning Result
    *** Checking out Result at "3.2.3"
    *** Checking out ReactiveCocoa at "6.0.1"
    *** Checking out ReactiveSwift at "2.0.1"
    

    carthage 会自动下载对应的frameworks,主要有:
    ReactiveCocoaReactiveSwiftResult三个对应框架

    下载完成后参考我的另外一篇carthage集成添加到项目中。

    • Cocoapods 安装

    项目目录下执行

    $ touch Podfile
    $ vim Podfile
    

    并直接写入Podfile

    platform:ios, '8.0'
    use_frameworks!
    target "yourtarget" do
    ...
    pod 'ReactiveCocoa','~>6.0'
    end
    

    保存后直接执行,注意我的版本,可能与实际的版本有差别。

    $ pod install
    ...
    

    swift 中使用 RAC + MVVM

    第一步:引入头文件

    import Result
    import ReactiveSwift
    import ReactiveCocoa
    

    第二步:创建服务及信号管道

    class LoginService {
        let (requestSignal, requestObserver) = Signal<String, NoError>.pipe()
        
        func canUseAccount(_ string : String) -> SignalProducer<Bool, NoError> {
            return SignalProducer { observer, disposable in
                self.requestObserver.send(value: string)
                observer.send(value: true)
                observer.sendCompleted()
            }
        }
    }
    

    第三步:创建ViewModel

    创建LoginModel类,并添加构造方法传入服务

    class LoginModel {  
        // 添加Error子类,FormError
        struct FormError : Error {
            let reason : String
            
            static let invalidAccount = FormError(reason: "请输入正确的账号")
            static let mismatchAccount = FormError(reason: "账号输入不匹配")
            static let accountUnavaliable = FormError(reason: "账号已存在")
        }
        
        init(loginService : LoginService) {
        
        }
    }
    

    第四步:在ViewModel中添加属性、行为和信号量

    class LoginModel {  
    ...
        let account : ValidatingProperty<String, FormError>
        let accountConfirm : ValidatingProperty<String, FormError>
        let termsAccepted : MutableProperty<Bool>
        
        let submit : Action <(), (), FormError>
        
        let reasons : Signal<String, NoError>
    ...
    }
    

    第五步:初始化ViewModel的属性、行为和信号量

    ...
    init(loginService : LoginService) {
        // 账号属性,""指定input为String,判断账号是否有效,返回.valid则符合条件,.invalid(FormError)则输出不符合日志。
        account = ValidatingProperty("") { input in
            return input.hasSuffix("aha") ? .valid : .invalid(.invalidAccount)
        }
        
        // 确认账号属性,""指定input为String,判断验证的账号是否和账号输入框相同,通过with传入account属性的value,返回.valid则符合条件,.invalid(FormError)则输出不符合日志。
        accountConfirm = ValidatingProperty("", with: account) { input, account in
            return input == account ? .valid : .invalid(.mismatchAccount)
        }
        
        // 同意用户协议可变属性,初始化为false
        termsAccepted = MutableProperty(false)
        
        // 构建联合属性,联结确认账号属性的结果和用户协议属性,并映射为字符串,根据条件返回映射结果。
        let validatedAccount : Property<String?> = Property.combineLatest(accountConfirm.result, termsAccepted).map { account, accepted -> String? in
            return !account.isInvalid && accepted ? account.value : nil
        }
        
        // 提交表单行为,展开联合属性validatedAccount,通过登录服务判断账号是否可用。
        submit = Action(unwrapping: validatedAccount) { (account : String) in
            let userAccount = account
            return loginService.canUseAccount(userAccount).promoteError(FormError.self).attemptMap{ Result<(), FormError> ($0 ? () : nil , failWith: .accountUnavaliable)}
        }
        
        // 错误信息,联合信号,在主线程中调用,返回结果映射组合为字符串,以备及时刷新到UI上。
        reasons = Property.combineLatest(account.result, accountConfirm.result).signal.debounce(0.1, on: QueueScheduler.main)
            .map{[$0, $1].flatMap{ $0.error?.reason }.joined(separator: "\n")}
    }
    ...
    

    第六步:创建视图并绑定数据

    class ViewController: UIViewController {
        let loginService = LoginService()
        
        private var viewModel : LoginModel?
        
        @IBOutlet weak var accountField: UITextField!
        @IBOutlet weak var confirmField: UITextField!
        @IBOutlet weak var loginSwitch: UISwitch!
        @IBOutlet weak var loginBtn: UIButton!
        @IBOutlet weak var reasonLabel: UILabel!
        
        override func viewDidLoad() {
            super.viewDidLoad()
            customizeModel()
            customizeSubviews()
        }
    
        func customizeModel() {
            viewModel = LoginModel(loginService: loginService)
            
            loginService.requestSignal.observeValues{
                print("UserService.requestSignal: Username `\($0)`.")
            }
            
            viewModel?.submit.completed.observeValues {
                let a = UIAlertController(title: "恭喜恭喜", message: "大吉大利,晚上吃鸡", preferredStyle: .alert)
                a.addAction(UIAlertAction(title: "吃!", style: .cancel, handler: nil))
                self.present(a, animated: true, completion: nil)
            }
            
            viewModel?.account.result.signal.observeValues {
                print("account: Validation result - \($0 != nil ? "\($0!)" : "No validation has ever been performed.")")
            }
            
            viewModel?.accountConfirm.result.signal.observeValues {
                print("accountConfirm: Validation result - \($0 != nil ? "\($0!)" : "No validation has ever been performed.")")
            }
        }
        
        func customizeSubviews () {
            accountField.text = viewModel?.account.value
            confirmField.text = viewModel?.accountConfirm.value
            loginSwitch.isOn = false
            
            viewModel!.account <~ accountField.reactive.continuousTextValues.skipNil()
            viewModel!.accountConfirm <~ confirmField.reactive.continuousTextValues.skipNil()
            viewModel!.termsAccepted <~ loginSwitch.reactive.isOnValues
            reasonLabel.reactive.text <~ viewModel!.reasons
            loginBtn.reactive.pressed = CocoaAction(viewModel!.submit)
        }
    }
    

    第七步:HAVE FUN!

    相关文章

      网友评论

          本文标题:ReactiveCocoa 学习笔记

          本文链接:https://www.haomeiwen.com/subject/zzxedxtx.html