RxSwift 入坑好多天 - 終於有了一點理解

1、前言

  • 江湖上都在說如今就要趕忙學 swift 了,即將是 swift 的天下了。在 api 變化不大的狀況下,swift 做爲一門新的語言,集衆家之所長,普通編碼確實比 oc 要好用的多了
  • 老早就據說 MVVM 的概念及響應式函數式編程,微軟確實厲害。本身最近沒什麼事,就前來入坑了

2、學習方式

3、本身寫註冊登陸及 tableView 的一點理解

  • 關於觀察者被觀察者(Observable)發出序列
    • UI 控件在 RxCocoa 下某些屬性都是被觀察者(Observable),均可以發出序列,常見的有
      • 控件的 text 類型是 ControlProperty<String> ,最終遵循 ObservableType協議
      • 按鈕的點擊 tap 類型是 ControlEvent<Void>,最終遵循 ObservableType協議
    • 對於設置 UI 控件的一些 Bool 類型的屬性,如可輸入,可點擊,通常用 UIBindingObserver<UIElementType, Value>(遵循 ObserverType協議) 來生成觀察者,對接受的數據條件進行判斷是否能夠輸入、可點擊
    // MARK: RX 擴展 計算型屬性
    // textfield 根據展現驗證後的結果可否輸入,驗證過了才能輸入
    extension Reactive where Base: UITextField {
    
         var inputEnable: UIBindingObserver<Base, ValidationResult> {
            return UIBindingObserver(UIElement: base, binding: { 
                (textField, result) in
    
                textField.isEnabled = result.isValid
             })
         }
    }
    • 關於在 VM 中經常使用的 Subject
      • Variable、PublishSubject 是 Subject 的一種,可當觀察者被 bindTo,可當序列數據源 Observable
        • Variable 它不會由於錯誤終止也不會正常終止, 適合作數據源,能夠用於控件的 text 屬性
        • PublishSubject 與普通的Subject不一樣,在訂閱時並不當即觸發訂閱事件,而是容許咱們在任意時刻手動調用onNext(),onError(),onCompleted來觸發事件,能夠用於按鈕的點擊
    • 關於被觀察者(Observable)的一些經常使用的 api
      • map 不會產生新的序列
      • flatMapLatest 會產生新的序列
      • combineLatest 不會產生新的序列
  • 關於 MVVM 文件夾分類
    • 以前 MVC 的與 iOS 裏的 Controller、View 一一對應,很好理解,而 MVVM 裏 Controller 屬於 V 了,負責處理控制器跳轉和將 View 和 VM 綁定等,大部分的業務邏輯代碼都在 VM 裏,感受應該是這樣
      git

    • 自始至終感受 iOS 裏的 model 這一層很輕,有時僅僅是創建了模型類而已。感受應該是各類數據操做如數據庫查詢等都應該是 model 這一層的github

  • 關於** 雙向綁定 **
    • 首先要有一些控件,理清楚須要監聽這些控件的哪些屬性值
    • 而後 VM 裏創建好這些屬性值對應的 Subject
    • 通常控制器裏生成 VM 對象,將控件的 Observable 的屬性綁定到 VM 的 Subject 屬性上,這樣可在 VM 裏監聽到控件屬性值的改變,此時 Subject 是 Observer,完成一次綁定
    • 在 VM 內,將 Subject 變成 Observable,生成對應 VM 可被觀察者屬性(用屬性保存加工變換後的 Observable )。這裏 Subject 是 Observable,可經過 map 、filter 等各類操做,操做的數據就是 Subject 觀察到的序列,相應模塊的整個業務邏輯都在此處。
    • 在控制器裏,再將 VM 的可被觀察者屬性綁定到 UI 控件上,在此完成雙向綁定
    override func viewDidLoad() {
        super.viewDidLoad()
        let regiestViewModel = RegiestViewModel()
        // 這裏作綁定: UI控件 --> VM    VM -> UI控件
        // 1.UI控件 --> VM
        nameTextField.rx.text.orEmpty
            .bindTo(regiestViewModel.username)
            .addDisposableTo(disposeBag)
        pwdTextField.rx.text.orEmpty
            .bindTo(regiestViewModel.userPwd)
            .addDisposableTo(disposeBag)
        repeatPwdTextField.rx.text.orEmpty
            .bindTo(regiestViewModel.repeatPwd)
            .addDisposableTo(disposeBag)
        regiestBtn.rx.tap
            .bindTo(regiestViewModel.registerTaps)
            .addDisposableTo(disposeBag)
        // 2.VM -> UI控件
        // 顯示結果的 label 上
        regiestViewModel.usernameValid
            .bindTo(nameTipLabel.rx.validResult)
            .addDisposableTo(disposeBag)
        // 綁定 密碼框是否能夠輸入
        regiestViewModel.usernameValid
            .bindTo(pwdTextField.rx.inputEnable)
            .addDisposableTo(disposeBag)
        regiestViewModel.passwordValid
            .bindTo(pwdTipLabel.rx.validResult)
            .addDisposableTo(disposeBag)
        regiestViewModel.passwordValid
            .bindTo(repeatPwdTextField.rx.inputEnable)
            .addDisposableTo(disposeBag)
        regiestViewModel.repeatPwdValid
            .bindTo(repeatPwdTipLabel.rx.validResult)
            .addDisposableTo(disposeBag)
        // 按鈕不是綁定, 按鈕是 subcribe, 須要操做的
        regiestViewModel.registerButtonEnabled
            .subscribe (onNext: { [weak self]  (result) in
                self?.regiestBtn.isEnabled = result
                self?.regiestBtn.alpha = result ? 1 : 0.8
                })
            .addDisposableTo(disposeBag)
        // 註冊結果 : 註冊成果或失敗 要展現在 UI 上
        regiestViewModel.registeResult
            .subscribe(onNext:{ [weak self] result in
                switch result {
                    case let .failed(message):
                        self?.showAlter(message: message)
                    case let .ok(message):
                        self?.showAlter(message: message)
                    case .empty:
                        self?.showAlter(message: "")
                    }
                })
            .addDisposableTo(disposeBag)
        // 跳轉到登陸界面按鈕的點擊
        loginVcBtn.rx.tap
            .subscribe(onNext: {
                let loginVc = LoginViewController()
                loginVc.title = "請登陸"
                self.navigationController?.pushViewController(loginVc, animated: true)
            })
            .addDisposableTo(disposeBag)
    }
  • 關於 tableView
    • 這裏須要 RxDataSources 這個配套的框架
    • 控制器裏須要一個 dataSource
      swift let dataSource = RxTableViewSectionedReloadDataSource<SectionModel<String, HerosItem>>()
    • 控制器裏用這個 dataSource 配置 cell數據庫

      dataSource.configureCell = { (_, tableView, indexPath, item) in
          var cell = tableView.dequeueReusableCell(withIdentifier: "herosCell")
          if cell == nil {
              cell = UITableViewCell(style: .subtitle, reuseIdentifier: "herosCell")
          }
          cell!.imageView?.image = UIImage(named: item.icon)
          cell!.textLabel?.text = item.name
          cell!.detailTextLabel?.text = item.intro
          return cell!
      }
    • 用 VM 建立出數據 Observable,發出序列,綁定到dataSource 上,完成數據的綁定編程

      homeViewMode.getSearchResult()
          .bindTo(tableView.rx.items(dataSource: dataSource))
          .addDisposableTo(disposeBag)
    • 其餘操做
      • tableView 的代理
        swift tableView.rx .setDelegate(self) .addDisposableTo(disposeBag)
      • tableView cell 的點擊
        tableView.rx.itemSelected .map { [weak self] indexPath in return (indexPath, self?.dataSource[indexPath]) } .subscribe(onNext: {(indexPath, item) in self.showAlter(item: item) }) .addDisposableTo(disposeBag)
    • 感受像一個固定的代碼模式,將數據源的代碼都移到 VM 裏了
    • 另外若是作實時搜索的話,用雙向綁定效果那是極好的,將搜索框的搜索關鍵字綁定到 VM 裏,在用 VM 產生序列綁定到 tableView 上swift

4、本例 demo 地址

5、其餘

  • MVVM 的使用並無那麼普及,大多數仍是 MVC,關於 MVC 的理解和減小控制器的代碼量和維護難度是一個難題,有篇文章可參考 http://www.infoq.com/cn/articles/rethinking-mvc-mvvm
  • 對於 RxSwift,像 swift 裏的可選類型的處理及 swift 裏的多種閉包類型及簡寫,理解還急需提升;對 RxSwift 的 api 還要繼續熟悉,以及後面的多線程及整個項目都用 MVVM 部署還須要更多的實踐
相關文章
相關標籤/搜索