[翻譯]一個iOS開發(fā)人員的日常--RxSwift的使用

原文:https://realm.io/news/tryswift-Marin-Todorov-I-create-iOS-apps-is-RxSwift-for-me/

在本次try! Swift NYC talk活動中骆膝,Marin Todorov介紹了RxSwift在一個iOS開發(fā)人員日常工作中的使用案例(RxSwift是一個異步,基于事件處理的框架)吱晒。如果你希望以引入一個庫的成本來解決你大半部分的痛苦的話,那么這篇文章對你最合適不過柠逞。

簡單介紹

Reactive Extensions,簡稱Rx粥谬,大量采用了observable序列和link style操作運算进胯,是用來解決事件和異步處理的庫伐庭,使用這個庫粉渠,開發(fā)者處理異步數(shù)據(jù)流不要太爽。

Marin表示說似忧,我讀了Rx理論很多次渣叛,但是我也沒法搞清楚iOS開發(fā)日常中如何去使用它丈秩,我踩過一些坑盯捌,今天就想介紹一下我用Rx解決過的一些實際問題。

Rx響應式的應用

首先來看看Rx響應式的方面蘑秽。使用Rx的響應式饺著,數(shù)據(jù)改變后會直接push到你 而不需要你再次主動pull數(shù)據(jù)箫攀。

Array < String >

這個array,作為一個strings的集合類幼衰,有時候處理起來有點小麻煩靴跛,你需要去遍歷它其中每個子元素,根據(jù)子元素的情況去處理數(shù)據(jù)渡嚣,即便采用更Swifty的方法梢睛,比如forEach這樣的,也還是挺麻煩识椰,因為本質(zhì)都還一樣绝葡,你需要提供一個閉包,一些block的代碼腹鹉,得一個元素接一個元素的處理藏畅。

問題在于,你處理這個array的時候功咒,是在一個固定的時間點愉阎,而這個時間點的數(shù)據(jù)也是固定的。而有新數(shù)據(jù)加到這個array中的時候力奋,你是不知道的榜旦。

拿table view controller來舉例。它綁定一個含有三個元素的array景殷,用戶點擊增加的按鈕章办,array里數(shù)據(jù)變成四個了,但是界面上還是三個滨彻。你或許可以采用通知或者委托的方式來同步數(shù)據(jù)model和UI這樣藕届。但是如果添加數(shù)據(jù)恰好是個異步耗時的過程,那就有點麻煩了亭饵。

Observable < Array < String > >

Rx把數(shù)據(jù)封裝成一個observable類休偶,這樣一個類,可以讓你本來的數(shù)據(jù)有時間的跨度辜羊,簡單的說踏兜,是給數(shù)據(jù)加上一個"時間的維度"

在這個例子中,observable序列定義了每個元素需要做的處理方法八秃,例如:print每個元素碱妆。在初始化的時候,每個元素會執(zhí)行一遍這個方法昔驱。當有新元素加入的時候疹尾,會再執(zhí)行一遍這個方法,以前你需要不僅要定義管做什么事情,還要管什么時間做纳本,現(xiàn)在你只需要這些元素各自的處理方法窍蓝,然后一但數(shù)據(jù)有新的變化,會自動執(zhí)行處理方法繁成。

我覺得這就是Rx最牛逼的地方吓笙,把需要異步處理變成線性的,這樣就把事情簡化了巾腕。你都不需要考慮你現(xiàn)在有啥數(shù)據(jù)面睛,過去有啥數(shù)據(jù),將來可能都有啥情況尊搬,而只需要定義數(shù)據(jù)驅(qū)動的處理方法塊就可以了侮穿。在上面的例子中,你如果想在tableview里顯示一個list毁嗦,就只需要線性的碼好從數(shù)據(jù)到UI的渲染代碼亲茅。把list中元素加加減減,數(shù)據(jù)變化驅(qū)動的錯綜復雜事件和交互都交給RxSwift打點就好狗准。
Observables特別好用克锣,再舉個例子,你在text filed輸入的字符串需要在某個label中顯示腔长,你只要實現(xiàn)數(shù)據(jù)如何顯示到label的代碼就行了袭祟,其他用戶輸入事件的激活等其他環(huán)節(jié)就交給RxSwift。
有了RxSwift捞附,事情變得觸手可及巾乳,簡單,且線性
再考慮一個復雜點的情況鸟召,scrollview中的翻頁加載數(shù)據(jù)問題胆绊,如果啟用了RxSwift,你只需要實現(xiàn)的翻頁代碼就比較簡單了:只需要實現(xiàn)從服務器加載20條數(shù)據(jù)并加入到list當中的這段代碼即可欧募。每次scroll到底部都會執(zhí)行這段相同的代碼并顯示到界面上压状。這樣把精力簡單的集中在這段代碼塊就可以了。

Rx中的函數(shù)式

來看下這些通常帶有預先設計的跟继,偉大的observables類
我們之前的三個例子中种冬,每個里面都涉及到一個observable類。
例如text field中的text就轉(zhuǎn)化為Observable<String>舔糖。只要把text封裝到成一個observable娱两,所有關于text的變化都通過這個observable string像信號源一樣來發(fā)射出來。而table view controller的那個例子中金吗,數(shù)據(jù)源變成了一個Observable<Array<...>>十兢,一個包含string的array趣竣。在scrolling的例子中,并沒有數(shù)據(jù)纪挎,那就封裝成一個Observable<Void>,因為我們感興趣的只是用戶觸及scrollview底部的事件本身跟匆。
在這些情況中异袄,你主要操作的都是同一個類,不管里面包含什么數(shù)據(jù)源玛臂,array也好烤蜕,string也好,或者其他的什么也好迹冤,他們都是observables讽营,你只需要關注你對observables的操作,比如行為定義這些泡徙。這種對數(shù)據(jù)源的observable封裝類橱鹏,你可以在不同項目中通過復制粘貼來復用,或者抽象成一個基礎框架堪藐,這樣在具體的項目中莉兰,就只需要集中處理具體的數(shù)據(jù)類型了。
下面我們來看一個Rx入門的小例子:
有一個app礁竞,需要實現(xiàn)一個textfield糖荒,用戶輸入文字后能查詢到匹配的代碼倉庫。我們依然采用有偉大的有前途的observables.界面上是一個textfield模捂,它輸出一個Observable<String>捶朵。通過它,用戶每次輸入改變都能獲得一個新的String狂男;
observable的有個“filter”函數(shù)能幫我們忽略掉不符合條件的數(shù)據(jù)综看,比如,兩個字母就不要搜了岖食,搜出來的結(jié)果也沒什么關聯(lián)的寓搬。而且特別贊的一點是,filter這個函數(shù)返回值也仍然是個observable實例县耽。這意味著在結(jié)果上仍然可以執(zhí)行對應的函數(shù)句喷,分分鐘搞出鏈式語法。接下來我調(diào)用debounce函數(shù)兔毙。
debounce是用來干嘛的呢唾琼?它用來過濾太密集的一些事件,只取最后一個.比如用戶輸入的時候不需要每個字母敲進去都發(fā)送一個事件澎剥,只要稍等一下锡溯,取最后一個事件就行了。
下一個要介紹的是map函數(shù),這個函數(shù)---函如其名祭饭,把輸入數(shù)據(jù)映射成輸出數(shù)據(jù)的樣子芜茵。比如這個例子中,輸入一個string倡蝙,對應就得生成一個URL九串,那么從string到NSURLRequest就有一次map。經(jīng)過上述的函數(shù)鏈條寺鸥,每一次輸入都會引發(fā)一次輸出猪钮。
FlatMap函數(shù)會允許我們創(chuàng)建一個網(wǎng)絡請求,并等待服務器結(jié)果返回胆建。返回是一個NSData類型烤低,進行一次map函數(shù)處理,利用NSJSONSerialization轉(zhuǎn)化成Array<AnyObject>笆载。
如圖14所述是這個app整個的工作流扑馁。從textfield的鍵盤輸入,到數(shù)據(jù)校驗凉驻,到網(wǎng)絡請求檐蚜,再到數(shù)據(jù)轉(zhuǎn)化,最后得到一個代碼倉庫的列表沿侈,可能以Realm的方式存儲下來闯第。這個流程非常優(yōu)秀,因為它是線性的缀拭,很容易就知道一個步驟接下來的下一個步驟咳短,也不需要去管數(shù)據(jù)的protocol或者delegate那些看起來很分散的東西。所有的流程是序列化的蛛淋,一個接一個的發(fā)生咙好。
下面這個代碼從storyboard引出textfield的outlet,然后引入Rx框架褐荷,實現(xiàn)一下這段代碼

query.rx_text

  .filter {string in
      return string.characters.count > 3
  }

  .debounce(0.51, scheduler: MainScheduler.instance)

  .map {string in
      let apiURL = NSURL(string: "https://api.github.com/q?=" + string)!
      return NSURLRequest(URL: apiURL)
  }

  .flatMapLatest { request in
      return NSURLSession.sharedSession().rx_data(request)
  }

  .map { data —> Array<AnyObject> in
      let json = try NSJSONSerialization.JSONObjectWithData(data, options: [])
      return json as! Array<AnyObject>
  }

  .map {object in
      return Repo(object: object)
  }

  .bindTo(tableView.rx_itemsWithCellIdentifier("Cell"))

我們調(diào)用filter函數(shù)勾效,和filter函數(shù)中實現(xiàn)的函數(shù)體,然后調(diào)用debounce叛甫,設置有效事件的時間間隔层宫,然后調(diào)用map,把string映射到URL然后是URLRequest其监,然后調(diào)用了FlatMap函數(shù)萌腿,把其中每個元素都調(diào)起一個URLSession來發(fā)起網(wǎng)絡請求,數(shù)據(jù)從服務器返回來后抖苦,再發(fā)起一次map的調(diào)用通過NSJASONSerialization來處理NSData毁菱,最終轉(zhuǎn)換成repo對象米死。在鏈條的最末端調(diào)用的bindTo可以把輸出的repo數(shù)據(jù)列表綁定到tableview中,直接調(diào)用了tableView的CellIdentifier函數(shù)就可以完成綁定贮庞。

看起來這些代碼又簡單又短并不是啥代碼峦筒,但是可以引發(fā)一些好的思考。你花了15分鐘體驗了一下Rx思維窗慎。這些簡單的代碼告訴你用Rx你可以干的事情物喷。這些代碼段都是線性完成的,一看就知道一塊代碼接下去的下一段代碼是要干啥捉邢。團隊的新成員很容易就讀懂了代碼邏輯脯丝,這種線性方式也能讓你很容易就看懂6個月前的代碼商膊。
最好的地方是我不是通過一個對象和另外一個對象的關系來調(diào)用代碼伏伐,而是鏈式的調(diào)用。鏈條中每一段代碼塊都有輸入輸出晕拆,也依賴上游的輸出藐翎,影響下游的輸入。上下游之間是緊密相連的实幕。一旦編譯成功吝镣,整個鏈條就不接受任何改變從而保證代碼過程順利的運行。

函數(shù)響應式App框架

這個話題跟我的iOS程序有多大關系呢昆庇。
我們來看看一個更復雜點的需要彈出新ViewController的情況末贾。在app中有一個NavtigationController,其中有個包含repos列表的tableview整吆,一個添加新repo的模態(tài)方式的viewcontroller拱撵。用戶通過鍵盤添加信息,點擊“Done”按鈕表蝙,然后數(shù)據(jù)需要發(fā)生變化拴测。平常,我們的解決辦法是實現(xiàn)一個delegate,并在協(xié)議里定義viewcontroller之間的交互和調(diào)用方法,這顯然是有點麻煩的「撸現(xiàn)在來試試新的解決方案集索。
我們有個全局的類,通過它我們可以跟任何類進行交互汇跨。這個類就是我們的observable务荆。比如在Add Repo的viewcontroller中可以包含一個observable屬性。每次用戶點擊done之后就發(fā)射出新加入的數(shù)據(jù)穷遂,那么事情會變得非常簡單蛹含。
下面這段源代碼從點擊右上角的“+”按鈕開始。在Rx的框架中塞颁,tap會返回來一個observable浦箱,每次用戶點擊+按鈕都會產(chǎn)生一次動作吸耿。

  addBarItem.rx_tap

    .debounce(0.5, scheduler: MainScheduler.instance)

    .flatMapFirst {[weak self] _ —> Observable<Repo> in
        let addVC = AddRepoViewController()
        self?.presentViewController(addVC, animated: true, completion: nil)
        return addVC.newRepo.asObservable()
    }

    .doOn {_ in
        self.dismissViewControllerAnimated(true, completion: nil)
    }

    .subscribeNext {repo in
        repos.value.append(repo)
    }

repos.asObservable()
    .bindTo(tableView.rx_itemsWithCellIdentifier("Cell"))

這里我又用到了debounce函數(shù),用來避免按鈕被多次點擊酷窥。如果用戶手欠點“+”點的特別快咽安,那么可能打開多個viewcontroller,rx框架里用debounce函數(shù)就避免了這種情況蓬推,只會打開一次妆棒。
來看看FlatMap在里面干了什么,之前的例子當中沸伏,我們用它來做了一些耗時的處理工作糕珊。這也是可以應用到這里的,present一個viewcontroller然后等待直到它被關閉毅糟,通過暴露出一個observable屬性红选,來返回一個新的repo。然后再關閉這個viewctroller姆另。在鏈條的最后對返回的數(shù)據(jù)進行處理喇肋。在這個例子中,新生成的repo返回后需要觸發(fā)前一個列表窗口的數(shù)據(jù)更新迹辐。所以這里蝶防,我們把repos列表綁定到了tableview中。
這樣的MVVM的模式相當快速明吩,只要生成一些viewcontrollers间学,暴露一些models來驅(qū)動他們的數(shù)據(jù),這就是你主要引入Rx代碼的地方印荔,通過數(shù)據(jù)驅(qū)動低葫,在這個鏈條的最后可以吧數(shù)據(jù)渲染到UI上。這個過程串起來相當清晰的躏鱼。
業(yè)務邏輯和界面的代碼分離的很清楚氮采。所有的邏輯代碼都在view model中。所以可以在這一層寫case做測試染苛。也不需要涉及到viewcontroller的初始化這些東東鹊漠。這樣的模式里,模塊的邊界變得顯而易見茶行。

RxSwift

RxSwift是一個長著同步臉的異步框架
它有函數(shù)式編程的一面躯概,用來處理異步事件,比如各種轉(zhuǎn)換還有別的東東畔师。也引入了很好的架構(gòu)模式娶靡,其實跟iOS的開發(fā)關聯(lián)可以非常緊密的。

進一步的資料可參考

ReactiveX.io

Rx有多種平臺多種語言的實現(xiàn)看锉,在這個網(wǎng)站上你可以找到一些官方的API說明姿锭,包含Swift塔鳍,Java,JS和Skala版本哦

RXSwift.org,rx-marin.com

這里有我寫的一些關于Rx的東東呻此,可以看看轮纫,加深理解

最后,非常感謝鼓勵我的Ash Furrow焚鲜,幫助我理解基礎要點的Jens Ravens掌唾,早期幫我改代碼的Florent Pillet,跟我一起玩Rx的朋友忿磅,Junior Bontognali糯彬,以及發(fā)起RxSwift項目的牛人Krunoslav Zaher

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市葱她,隨后出現(xiàn)的幾起案子撩扒,更是在濱河造成了極大的恐慌,老刑警劉巖览效,帶你破解...
    沈念sama閱讀 221,198評論 6 514
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件却舀,死亡現(xiàn)場離奇詭異虫几,居然都是意外死亡锤灿,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,334評論 3 398
  • 文/潘曉璐 我一進店門辆脸,熙熙樓的掌柜王于貴愁眉苦臉地迎上來但校,“玉大人,你說我怎么就攤上這事啡氢∽创眩” “怎么了?”我有些...
    開封第一講書人閱讀 167,643評論 0 360
  • 文/不壞的土叔 我叫張陵倘是,是天一觀的道長亭枷。 經(jīng)常有香客問我,道長搀崭,這世上最難降的妖魔是什么叨粘? 我笑而不...
    開封第一講書人閱讀 59,495評論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮瘤睹,結(jié)果婚禮上升敲,老公的妹妹穿的比我還像新娘。我一直安慰自己轰传,他們只是感情好驴党,可當我...
    茶點故事閱讀 68,502評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著获茬,像睡著了一般港庄。 火紅的嫁衣襯著肌膚如雪倔既。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,156評論 1 308
  • 那天鹏氧,我揣著相機與錄音叉存,去河邊找鬼。 笑死度帮,一個胖子當著我的面吹牛歼捏,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播笨篷,決...
    沈念sama閱讀 40,743評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼瞳秽,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了率翅?” 一聲冷哼從身側(cè)響起练俐,我...
    開封第一講書人閱讀 39,659評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎冕臭,沒想到半個月后腺晾,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,200評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡辜贵,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,282評論 3 340
  • 正文 我和宋清朗相戀三年悯蝉,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片托慨。...
    茶點故事閱讀 40,424評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡鼻由,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出厚棵,到底是詐尸還是另有隱情蕉世,我是刑警寧澤,帶...
    沈念sama閱讀 36,107評論 5 349
  • 正文 年R本政府宣布婆硬,位于F島的核電站狠轻,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏彬犯。R本人自食惡果不足惜向楼,卻給世界環(huán)境...
    茶點故事閱讀 41,789評論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望躏嚎。 院中可真熱鬧蜜自,春花似錦、人聲如沸卢佣。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,264評論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽虚茶。三九已至戈鲁,卻和暖如春仇参,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背婆殿。 一陣腳步聲響...
    開封第一講書人閱讀 33,390評論 1 271
  • 我被黑心中介騙來泰國打工诈乒, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人婆芦。 一個月前我還...
    沈念sama閱讀 48,798評論 3 376
  • 正文 我出身青樓怕磨,卻偏偏與公主長得像,于是被迫代替她去往敵國和親消约。 傳聞我的和親對象是個殘疾皇子肠鲫,可洞房花燭夜當晚...
    茶點故事閱讀 45,435評論 2 359

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

  • 最近在學習RxSwift相關的內(nèi)容,在這里記錄一些基本的知識點或粮,以便今后查閱导饲。 Observable 在RxSwi...
    L_Zephyr閱讀 1,762評論 1 4
  • 前言 看了前幾篇關于RxSwift主要概念的文章,會對RxSwift有個大致的了解氯材,這篇文章會詳細講述如何使用Rx...
    最Fly的Engine人閱讀 13,044評論 3 40
  • 本文章內(nèi)部分圖片資源來自RayWenderlich.com 本文結(jié)合自己的理解來總結(jié)介紹一下RxSwift最基本的...
    FKSky閱讀 2,887評論 4 14
  • 發(fā)現(xiàn) 關注 消息 RxSwift入坑解讀-你所需要知道的各種概念 沸沸騰關注 2016.11.27 19:11*字...
    楓葉1234閱讀 2,800評論 0 2
  • 今天迎來了久違的太陽渣锦,天氣很好,經(jīng)過了兩天的雨水氢哮,天空晴朗袋毙,沒有霧霾,早上出門還看到了日月同輝的景象命浴,心情很好娄猫,今...
    FineYoga章艷15111閱讀 118評論 0 0