ReactiveCocoa 學(xué)習(xí)筆記

ReactiveCocoa

配置安裝RAC

  • Carthage 安裝

項(xiàng)目目錄下執(zhí)行

$ touch Cartfile
$ vim Cartfile

并直接寫(xiě)入Cartfile

github "ReactiveCocoa/ReactiveCocoa" ~> 6.0

保存后直接執(zhí)行,注意我的版本,可能與實(shí)際的版本有差別。

$ 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 會(huì)自動(dòng)下載對(duì)應(yīng)的frameworks,主要有:
ReactiveCocoa、ReactiveSwift、Result三個(gè)對(duì)應(yīng)框架

下載完成后參考我的另外一篇carthage集成添加到項(xiàng)目中。

  • Cocoapods 安裝

項(xiàng)目目錄下執(zhí)行

$ touch Podfile
$ vim Podfile

并直接寫(xiě)入Podfile

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

保存后直接執(zhí)行,注意我的版本,可能與實(shí)際的版本有差別。

$ pod install
...

swift 中使用 RAC + MVVM

第一步:引入頭文件

import Result
import ReactiveSwift
import ReactiveCocoa

第二步:創(chuàng)建服務(wù)及信號(hào)管道

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()
        }
    }
}

第三步:創(chuàng)建ViewModel

創(chuàng)建LoginModel類(lèi),并添加構(gòu)造方法傳入服務(wù)

class LoginModel {  
    // 添加Error子類(lèi),F(xiàn)ormError
    struct FormError : Error {
        let reason : String
        
        static let invalidAccount = FormError(reason: "請(qǐng)輸入正確的賬號(hào)")
        static let mismatchAccount = FormError(reason: "賬號(hào)輸入不匹配")
        static let accountUnavaliable = FormError(reason: "賬號(hào)已存在")
    }
    
    init(loginService : LoginService) {
    
    }
}

第四步:在ViewModel中添加屬性、行為和信號(hào)量

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的屬性、行為和信號(hào)量

...
init(loginService : LoginService) {
    // 賬號(hào)屬性,""指定input為String,判斷賬號(hào)是否有效,返回.valid則符合條件,.invalid(FormError)則輸出不符合日志。
    account = ValidatingProperty("") { input in
        return input.hasSuffix("aha") ? .valid : .invalid(.invalidAccount)
    }
    
    // 確認(rèn)賬號(hào)屬性,""指定input為String,判斷驗(yàn)證的賬號(hào)是否和賬號(hào)輸入框相同,通過(guò)with傳入account屬性的value,返回.valid則符合條件,.invalid(FormError)則輸出不符合日志。
    accountConfirm = ValidatingProperty("", with: account) { input, account in
        return input == account ? .valid : .invalid(.mismatchAccount)
    }
    
    // 同意用戶協(xié)議可變屬性,初始化為false
    termsAccepted = MutableProperty(false)
    
    // 構(gòu)建聯(lián)合屬性,聯(lián)結(jié)確認(rèn)賬號(hào)屬性的結(jié)果和用戶協(xié)議屬性,并映射為字符串,根據(jù)條件返回映射結(jié)果。
    let validatedAccount : Property<String?> = Property.combineLatest(accountConfirm.result, termsAccepted).map { account, accepted -> String? in
        return !account.isInvalid && accepted ? account.value : nil
    }
    
    // 提交表單行為,展開(kāi)聯(lián)合屬性validatedAccount,通過(guò)登錄服務(wù)判斷賬號(hào)是否可用。
    submit = Action(unwrapping: validatedAccount) { (account : String) in
        let userAccount = account
        return loginService.canUseAccount(userAccount).promoteError(FormError.self).attemptMap{ Result<(), FormError> ($0 ? () : nil , failWith: .accountUnavaliable)}
    }
    
    // 錯(cuò)誤信息,聯(lián)合信號(hào),在主線程中調(diào)用,返回結(jié)果映射組合為字符串,以備及時(shí)刷新到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")}
}
...

第六步:創(chuàng)建視圖并綁定數(shù)據(jù)

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!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容