RxSwift入坑筆記

自學Swift有一段時間了,在一個技術群里偶然聽到RxSwift的概念逻卖,了解了以后,覺得很有必要學一學昭抒。但是開始接觸真的比較難理解评也。從網(wǎng)上找了一些資料以后開始了RxSwift之旅。
主要資料參考如下(感謝分享的力量):
http://www.codertian.com/2016/12/10/RxSwift-shi-zhan-jie-du-base-demo/
http://www.codertian.com/2016/11/27/RxSwift-ru-keng-ji-read-document/
博主寫的很好灭返,所以沒必要再照著寫一遍盗迟。這邊文章主要針對上面的博客寫一些自己的理解以及總結(jié)吧。

RxSwift實戰(zhàn)這篇文章中熙含,是基于MVVM寫的罚缕。其中用到的文件有Service,ViewModel怎静,Protocol邮弹,ViewController(頁面比較簡單,并未涉及到View蚓聘。)

Service主要負責一些網(wǎng)絡請求和一些數(shù)據(jù)的訪問操作腌乡,供ViewModel使用。

注冊頁面三個文本框加一個按鈕夜牡,要實現(xiàn)的效果如下与纽。
用戶名和密碼都有一定格式要求。


注冊頁面.png

先把整體的流程貼在這里塘装。
1.將用戶名的TextField流做為Observable(被觀察者)綁定到ViewModel的userName急迂。
其中userName聲明為

 let username = Variable<String>("")    //初始值為""

Variable是BehaviorSubject一個包裝箱,就像是一個箱子一樣蹦肴,使用的時候需要調(diào)用asObservable()拆箱僚碎,里面的value是一個BehaviorSubject,他不會發(fā)出error事件冗尤,但是會自動發(fā)出completed事件听盖。

Variable和BehaviorSubject都是Subjects概念的范疇胀溺,具體可參考http://www.codertian.com/2016/11/27/RxSwift-ru-keng-ji-read-document/。此處理解為把userName聲明為一個Observable即可皆看。

2.將TextFeld的text綁定到userName上

usernameTextField.rx.text.orEmpty
        .bindTo(viewModel.username)
        .addDisposableTo(disposeBag)
// usernameTextField.rx.text.orEmpty是RxCocoa庫中概念仓坞,把TextFiled的text變成了一個Observable,orEmpty把String?過濾nil
// 變?yōu)镾tring類型腰吟。
  1. 在vewModel中處理userName
 let usernameUsable: Observable<Result>   // 這個是對userName處理以后的輸出output
// 如果只是檢測格式无埃,那么可以寫成如下形式

usernameUsable = userName.asObservable().map.({(userName) -> Result  in  
    if userName.characters.count == 0 {
      return .empty
    }
    if userName.characters.count < characterCount {
      return failed(message:"長度至少6個字符")
    }
    return just(.ok(message:"用戶名可用")
}).shareReplay(1)
// map函數(shù)是通過傳入一個函數(shù)閉包把原來的sequence轉(zhuǎn)變?yōu)橐粋€新的sequence的操作

如果這其中涉及到了網(wǎng)絡請求,或者是耗時的讀取數(shù)據(jù)庫操作毛雇,那么我們會用到flatMap函數(shù)
flatMap將一個sequence轉(zhuǎn)換為一個sequences嫉称,當你接收一個sequence的事件,你還想接收其他sequence發(fā)出的事件的話可以使用flatMap灵疮,她會將每一個sequence事件進行處理以后织阅,然后再以一個新的sequence形式發(fā)出事件。

具體參考:
http://www.codertian.com/2016/12/01/RxSwift-ru-keng-ji-learn-the-difficulty/
如果我們要檢測用戶名是否已經(jīng)存在震捣,需要去發(fā)送網(wǎng)絡請求或者去查詢本地數(shù)據(jù)庫荔棉,那么就要用flatMap

usernameUsable = userName.asObservable().flatMap({(userName) in 
  return service.validateUsername(username)
                .observeOn(MainScheduler.instance)
                .catchErrorJustReturn(.failed(message: "username檢測出錯")
 })

要注意的是
Map 中的閉包返回的是一個經(jīng)過閉包參數(shù)userName(這也是原始序列的元素)處理過后的Result類型的元素。
flatMap中的閉包返回的是一個 Observable<Result> 序列蒿赢,例如
service中validateUsername中返回的 .just(.failed(message: "號碼長度至少6個字符"))

service中validateUsername方法如下

func validateUsername(_ username: String) -> Observable<Result> {
        
        if username.characters.count == 0 {//當字符等于0的時候什么都不做
            return .just(.empty)
        }
        
        if username.characters.count < minCharactersCount {//當字符小于6的時候返回failed
            return .just(.failed(message: "號碼長度至少6個字符"))
        }
        
        if usernameValid(username) {//檢測本地數(shù)據(jù)庫中是否已經(jīng)存在這個名字
            return .just(.failed(message: "賬戶已存在"))
        }
        
        return .just(.ok(message: "用戶名可用"))
    }

觀察map和flatMap函數(shù)定義

public func map<R>(_ transform: @escaping (Self.E) throws -> R) -> RxSwift.Observable<R>

public func flatMap<O : ObservableConvertibleType>(_ selector: @escaping (Self.E) throws -> O) -> RxSwift.Observable<O.E>

其實這里的map和flatMap的作用是一樣的润樱。map函數(shù)可以對原有序列里面的事件元素進行改造,返回的還是原來的序列羡棵。而flatMap對原有序列中的元素進行改造和處理壹若,每一個元素返回一個新的sequence,然后把每一個元素對應的sequence合并為一個新的sequence序列皂冰。

觀察fatMap店展,聲明了一個遵循ObservableConvertibleType協(xié)議的泛型類型O(可以理解為一個序列)。閉包中的參數(shù)是原始序列的元素(在這指的是userName:String)灼擂,閉包返回的是遵循ObservableConvertibleType協(xié)議的泛型類型O壁查,也就是說返回的是一個序列。最后函數(shù)返回的是O類型的元素組成的一個序列:Observable<O.E>剔应,在這里睡腿,O.E指的是Result類型,也就是說flatMap函數(shù)最后返回的是O類型的峻贮。
(有理解的不對的地方席怪,歡迎指正)。

viewModel和Service中工作完成以后纤控,可以在controller寫后續(xù)工作了

思考挂捻?如何讓userNameUsable綁定到提示用戶名是否可用的Label呢?
當然可以用subscribe(onNext:)方法

    viewModel.userNameUsable.subscribe(onNext: { [weak self] (result) in
      switch result {
      case .ok(let message):
        self!.nameTipLabel.text = message
      case .empty:
        self!.nameTipLabel.text = ""
      case .failed(let message):
        self!.nameTipLabel.text = message
      }
    }).addDisposableTo(disposeBag)

但是這樣寫有一些繁瑣船万,畢竟這些放在viewController里面寫不太優(yōu)雅刻撒,如果要在Result不同情況下顯示不同顏色骨田,那么代碼量又會進一步增多。

想要解決這個声怔,就需要用到UIBindingObserver了态贤,這個是個很有用的東西。
我們現(xiàn)在為UILabel自定義一個validationResult

extension Reactive where Base: UILabel {
    var validationResult: UIBindingObserver<Base, Result> {
        return UIBindingObserver(UIElement: base) { label, result in
            label.textColor =  根據(jù)result處理
            label.text = 根據(jù)result處理
        }
    }
}

// 自定義了一個Observer醋火,對UIlabel進行了擴展悠汽,根據(jù)result結(jié)果,進行他的text和textColor的顯示

詳細內(nèi)容見http://www.codertian.com/2016/12/01/RxSwift-ru-keng-ji-learn-the-difficulty/

然后viewController中可以寫成如下形式芥驳,優(yōu)雅了很多吧柿冲!

viewModel.userNameUsable
       .bind(to: nameTipLabel.rx.validationResult)
       .addDisposableTo(disposeBag)

RxSwift中除了Observable,還有一個概念是Driver

下面來看一下Driver
其實Driver和Observable的使用結(jié)構(gòu)是一樣的只是Driver和Observable有點區(qū)別兆旬,Driver是RxSwift專門針對UI操作假抄,而Observable是一個通用的東西

下面是登錄頁面的Driver使用
頁面效果如下

登錄頁面.png

同樣先寫Service,這里如果要校驗一下userName是否是已經(jīng)存在的話爵憎,那么要用flatMap慨亲,因為要有耗時操作,如果只是校驗一下格式的話宝鼓,那么要用map。

假設這里需要校驗用戶名是否已存在巴刻。那么
在viewModel中聲明輸入如下:

// input
let userName = Observable<String>("")
// output
let userNameUsable: Driver<Result>

ViewModel的init方法中初始化userNameUsable

    userNameUsable = userName.asObservable()
                             .asDriver(onErrorJustReturn: "")
                             .flatMapLatest { username in
      return service.loginUserNameValid(username)
             .asDriver(onErrorJustReturn:
             .failed(message: "連接server失敗"))
    }

在ViewController里面進行綁定

    viewModel.userNameUsable.drive(nameTipLabel.rx.validationResult)
                            .addDisposableTo(disposeBag)

如果沒有自定義Observer愚铡,那么寫法如下:

    viewModel.userNameUsable.drive(onNext: { [weak self] (result) in
      switch result {
      case let .ok(message):
        // 處理
      case .empty:
        // 處理
      case let .failed(message):
        // 處理
    }).addDisposableTo(disposeBag)

注意:只有Driver可以使用drive方法,Observable是不能使用的胡陪。

另外官方的寫法是這樣的:
viewModel初始化方法如下

init(input: (userName: Driver<String>, password: Driver<String>, loginTaps: Driver<Void>), service: ValidateService)
// init的參數(shù)使用一個元組

viewController里面

let viewModel = LoginViewModel(input: (userName: nameTextField.rx.text.orEmpty.asDriver(), password: passWordTextField.rx.text.orEmpty.asDriver(), loginTaps:doSomething.rx.tap.asDriver() ), service: ValidateService.instance)
    
viewModel.userNameUsable.drive(nameTipLabel.rx.validationResult)
                            .addDisposableTo(disposeBag)

還有剩下的列表頁沥寥,使用流程和上述方式并無二致∧可參考博主原文邑雅。
總之還是要多寫。否則看過幾天之后便全忘記了妈经。
最后感謝原文博主分享淮野。

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市吹泡,隨后出現(xiàn)的幾起案子骤星,更是在濱河造成了極大的恐慌,老刑警劉巖爆哑,帶你破解...
    沈念sama閱讀 218,858評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件洞难,死亡現(xiàn)場離奇詭異,居然都是意外死亡揭朝,警方通過查閱死者的電腦和手機队贱,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評論 3 395
  • 文/潘曉璐 我一進店門色冀,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人柱嫌,你說我怎么就攤上這事锋恬。” “怎么了慎式?”我有些...
    開封第一講書人閱讀 165,282評論 0 356
  • 文/不壞的土叔 我叫張陵伶氢,是天一觀的道長。 經(jīng)常有香客問我瘪吏,道長癣防,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,842評論 1 295
  • 正文 為了忘掉前任掌眠,我火速辦了婚禮蕾盯,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘蓝丙。我一直安慰自己级遭,他們只是感情好,可當我...
    茶點故事閱讀 67,857評論 6 392
  • 文/花漫 我一把揭開白布渺尘。 她就那樣靜靜地躺著挫鸽,像睡著了一般。 火紅的嫁衣襯著肌膚如雪鸥跟。 梳的紋絲不亂的頭發(fā)上丢郊,一...
    開封第一講書人閱讀 51,679評論 1 305
  • 那天,我揣著相機與錄音医咨,去河邊找鬼枫匾。 笑死,一個胖子當著我的面吹牛拟淮,可吹牛的內(nèi)容都是我干的干茉。 我是一名探鬼主播,決...
    沈念sama閱讀 40,406評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼很泊,長吁一口氣:“原來是場噩夢啊……” “哼角虫!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起撑蚌,我...
    開封第一講書人閱讀 39,311評論 0 276
  • 序言:老撾萬榮一對情侶失蹤上遥,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后争涌,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體粉楚,經(jīng)...
    沈念sama閱讀 45,767評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了模软。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片伟骨。...
    茶點故事閱讀 40,090評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖燃异,靈堂內(nèi)的尸體忽然破棺而出携狭,到底是詐尸還是另有隱情,我是刑警寧澤回俐,帶...
    沈念sama閱讀 35,785評論 5 346
  • 正文 年R本政府宣布逛腿,位于F島的核電站,受9級特大地震影響仅颇,放射性物質(zhì)發(fā)生泄漏单默。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,420評論 3 331
  • 文/蒙蒙 一忘瓦、第九天 我趴在偏房一處隱蔽的房頂上張望搁廓。 院中可真熱鬧,春花似錦耕皮、人聲如沸境蜕。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,988評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽粱年。三九已至,卻和暖如春罚拟,著一層夾襖步出監(jiān)牢的瞬間逼泣,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,101評論 1 271
  • 我被黑心中介騙來泰國打工舟舒, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人嗜憔。 一個月前我還...
    沈念sama閱讀 48,298評論 3 372
  • 正文 我出身青樓秃励,卻偏偏與公主長得像,于是被迫代替她去往敵國和親吉捶。 傳聞我的和親對象是個殘疾皇子夺鲜,可洞房花燭夜當晚...
    茶點故事閱讀 45,033評論 2 355

推薦閱讀更多精彩內(nèi)容

  • 最近在學習RxSwift相關的內(nèi)容,在這里記錄一些基本的知識點呐舔,以便今后查閱币励。 Observable 在RxSwi...
    L_Zephyr閱讀 1,756評論 1 4
  • 前言 在之前用Objective-C語言做項目的時候,我習慣性的會利用MVVM模式去架構(gòu)項目珊拼,在框架Reactiv...
    Tangentw閱讀 21,194評論 32 123
  • 上一篇 2016海南之行(四)物產(chǎn)與水果 忽然之間多肉闖入我們的生活,俘獲了多少人的心仅胞,我是個后知后覺的人了每辟,關...
    自然旅行隨筆閱讀 911評論 12 3
  • 時間怕是個職業(yè)馬拉松選手,以前懵懂現(xiàn)在才慢慢發(fā)現(xiàn)它跑起來是那么有毅力且又是那么快干旧。 一天天渠欺,不知不覺已經(jīng)在這個北方...
    呆子先生閱讀 392評論 5 3