(WWDC) 初探 Combine


內(nèi)容概覽

  • 前言
  • Combine
  • Publishers
  • Subscribers
  • Operators





前言


假設(shè)你需要構(gòu)建如下應(yīng)用:

App要求:

  • 實時驗證用戶名是否有效
  • 匹配密碼
  • 響應(yīng)用戶界面

你會如何實現(xiàn)?實現(xiàn)的過程會應(yīng)用到哪些概念凿跳?



你需要使用 Target/Action 來獲取輸入框的最新值,使用計時器來實現(xiàn)定期檢查,使用 KVO 來控制加載指示器的狀態(tài)。

你還需要使用 URLSession 來進(jìn)行網(wǎng)絡(luò)請求,匯總?cè)齻€輸入框的結(jié)果癞松,然后通過 KVC 來控制按鈕的可用狀態(tài)。



涉及到的異步接口:

  • Target/Action
  • 通知中心
  • URLSession
  • KVC
  • 定制的回調(diào)



是否覺得入蛆,這些步驟略顯繁瑣响蓉?
是否有更好的解決方案呢?





Combine


Combine 提供了統(tǒng)一的哨毁、聲明式的API枫甲,可以隨著時間的推移處理多個值



Combine 特性:

  • 泛型
  • 類型安全
  • 組合優(yōu)先
  • 由請求驅(qū)動



核心概念:

  • Publishers
  • Subscribers
  • Operators





Publisher

  • 定義值和錯誤如何產(chǎn)生
  • 值類型
  • 允許 Subscriber 的注冊
protocol Publisher {

    associatedtype Output
    associatedtype Failure: Error
    
    func subscribe<S: Subscriber>(_ subscriber: S)
      where S.Input == Output, S.Failure == Failure
}

為 Publisher 拓展通知中心

extension NotificationCenter {
    struct Publisher: Combine.Publisher {
        typealias Output = Notification
        typealias Failure = Never
        init(center: NotificationCenter, name: Notification.Name, object: Any? = nil)
    }
}





Subscribers

  • 接收值和完成事件
  • 引用類型
protocol Subscriber {

    associatedtype Input
    associatedtype Failure: Error
    
    func receive(subscription: Subscription)
    func receive(_ input: Input) -> Subscribers.Demand
    func receive(completion: Subscribers.Completion<Failure>)
}
extension Subscribers {

    class Assign<Root, Input>: Subscriber, Cancellable {
        typealias Failure = Never
    }
    
    init(object: Root, keyPath: ReferenceWritableKeyPath<Root, Input>)
}



Publisher 和 Subscriber 之間的關(guān)系

  1. Subscriber 被綁定到 Publisher 上
  2. Publisher 發(fā)送一個訂閱
  3. Subscriber 向 Publisher 請求值
  4. Publisher 向 Subscriber 發(fā)送值
  5. Publisher 發(fā)送完成事件



使用 Publisher 和 Subscriber 的示例代碼:

class Wizard {
    var grade: Int
}

let merlin = Wizard(grade: 5)

let graduationPublisher = NotificationCenter.Publisher(center: .default, name: .graduated, object: merlin)
let gradeSubscriber = Subscribers.Assign(object: merlin, keyPath: \.grade)

graduationPublisher.subscribe(gradeSubscriber)

以上代碼由于類型不匹配,會遭遇如下錯誤:



看來扼褪,我們需要通過某種方式來實現(xiàn)類型匹配想幻。





Operators

  • 輸入 Publisher
  • 形容改變值的行為
  • 訂閱 Publisher (向上游)
  • 發(fā)送結(jié)果給 Subscriber (向下游)
  • 值類型
extension Publishers {
    struct Map<Upstream: Publisher, Output>: Publisher {
        typealias Failure = Upstream.Failure
        
        let upstream: Upstream
        let transform: (Upstream.Output) -> Output
    } 
}



現(xiàn)在,我們可以通過優(yōu)雅的方式解決之前的問題话浇。

let graduationPublisher = NotificationCenter.Publisher(center: .default, name: .graduated, object: merlin)
let gradeSubscriber = Subscribers.Assign(object: merlin, keyPath: \.grade)

let converter = Publishers.Map(upstream: graduationPublisher) { note in
    return note.userInfo?["NewGrade"] as? Int ?? 0
}

converter.subscribe(gradeSubscriber)

使用 Map Operator脏毯,我們完成了訂閱操作。

不過幔崖,還可以把步驟簡化一下:

extension Publisher {
    func map<T>(_ transform: @escaping (Output) -> T) -> Publishers.Map<Self, T> {
        return Publishers.Map(upstream: self, transform: transform)
    }
}

let cancellable = NotificationCenter.default.publisher(for: .graduated, object: merlin)
                    .map { note in
                        return note.userInfo?["NewGrade"] as? Int ?? 0
                    }
                    .assign(to: \.grade, on: merlin)


聲明式 Operator API 的優(yōu)點:

  • 函數(shù)式轉(zhuǎn)換
  • 鏈?zhǔn)讲僮?/li>
  • 錯誤處理
  • 線程和隊列操作
  • 調(diào)度和時間



在使用 Operator 時食店,優(yōu)先嘗試進(jìn)行組合

在對單個值(如:獨立的網(wǎng)絡(luò)請求)進(jìn)行異步操作時,推薦使用 Future
在對多個值(如:帳號密碼輸入框的事件流)進(jìn)行異步操作時赏寇,推薦使用 Publisher



使用 map 的部分其實還可以優(yōu)化

使用 compactMap 簡化操作:

進(jìn)行篩選:

截取部分值:



還可以組合多個 Publisher:

  • Zip
    • 將多個輸入的單次值組合后轉(zhuǎn)換為單個元組
    • 是一個"when/and" 操作吉嫩,所有輸入都有輸入值才能產(chǎn)生輸出
    • 要求具備所有輸入的輸入值時,才能產(chǎn)生輸入



  • CombineLatest
    • 將多個輸入的單次值組合后轉(zhuǎn)換為單個值
    • 是一個"when/or"嗅定,其中一個輸入有輸入值就能產(chǎn)生輸出
    • 只要有一個輸入有輸入值自娩,就會執(zhí)行輸出
    • 會存儲所有輸入的最后一個輸入值


使用建議
  • 使用 filter處理通知中心的通知
  • 使用 zip 處理等待兩個請求完成的事件
  • 使用 decode 處理 URLResponse 的 data


更多內(nèi)容

  • 錯誤處理和取消訂閱
  • 調(diào)度和時間
  • 設(shè)計模式


歡迎繼續(xù)閱讀 (WWDC) 實踐 Combine





參考內(nèi)容:
Introducing Combine




轉(zhuǎn)載請注明出處渠退,謝謝~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末忙迁,一起剝皮案震驚了整個濱河市脐彩,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌动漾,老刑警劉巖丁屎,帶你破解...
    沈念sama閱讀 211,348評論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異旱眯,居然都是意外死亡晨川,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,122評論 2 385
  • 文/潘曉璐 我一進(jìn)店門删豺,熙熙樓的掌柜王于貴愁眉苦臉地迎上來共虑,“玉大人,你說我怎么就攤上這事呀页÷璋瑁” “怎么了?”我有些...
    開封第一講書人閱讀 156,936評論 0 347
  • 文/不壞的土叔 我叫張陵蓬蝶,是天一觀的道長尘分。 經(jīng)常有香客問我,道長丸氛,這世上最難降的妖魔是什么培愁? 我笑而不...
    開封第一講書人閱讀 56,427評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮缓窜,結(jié)果婚禮上定续,老公的妹妹穿的比我還像新娘。我一直安慰自己禾锤,他們只是感情好私股,可當(dāng)我...
    茶點故事閱讀 65,467評論 6 385
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著恩掷,像睡著了一般倡鲸。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上黄娘,一...
    開封第一講書人閱讀 49,785評論 1 290
  • 那天旦签,我揣著相機與錄音,去河邊找鬼寸宏。 笑死,一個胖子當(dāng)著我的面吹牛偿曙,可吹牛的內(nèi)容都是我干的氮凝。 我是一名探鬼主播,決...
    沈念sama閱讀 38,931評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼望忆,長吁一口氣:“原來是場噩夢啊……” “哼罩阵!你這毒婦竟也來了竿秆?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,696評論 0 266
  • 序言:老撾萬榮一對情侶失蹤稿壁,失蹤者是張志新(化名)和其女友劉穎幽钢,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體傅是,經(jīng)...
    沈念sama閱讀 44,141評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡匪燕,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,483評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了喧笔。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片帽驯。...
    茶點故事閱讀 38,625評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖书闸,靈堂內(nèi)的尸體忽然破棺而出尼变,到底是詐尸還是另有隱情,我是刑警寧澤浆劲,帶...
    沈念sama閱讀 34,291評論 4 329
  • 正文 年R本政府宣布嫌术,位于F島的核電站,受9級特大地震影響牌借,放射性物質(zhì)發(fā)生泄漏度气。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,892評論 3 312
  • 文/蒙蒙 一走哺、第九天 我趴在偏房一處隱蔽的房頂上張望蚯嫌。 院中可真熱鬧,春花似錦丙躏、人聲如沸择示。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,741評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽栅盲。三九已至,卻和暖如春废恋,著一層夾襖步出監(jiān)牢的瞬間谈秫,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評論 1 265
  • 我被黑心中介騙來泰國打工鱼鼓, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留拟烫,地道東北人。 一個月前我還...
    沈念sama閱讀 46,324評論 2 360
  • 正文 我出身青樓迄本,卻偏偏與公主長得像硕淑,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,492評論 2 348

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

  • RxJava詳解(一) 年初的時候就想學(xué)習(xí)下RxJava然后寫一些RxJava的教程置媳,無奈發(fā)現(xiàn)已經(jīng)年底了于樟,然而我還...
    CharonChui閱讀 2,410評論 0 0
  • 在正文開始之前的最后,放上GitHub鏈接和引入依賴的gradle代碼: Github: https://gith...
    蘇蘇說zz閱讀 677評論 0 2
  • 當(dāng)前理財_華融道理財 當(dāng)前理財_華融道理財 當(dāng)前理財_華融道理財
    樊乙補11085閱讀 228評論 0 0