軟體設計

Swift 觀察者模式:從 NotificationCenter 到 Combine

開發 iOS App 時,總會遇到這種情況:A 畫面發生變更,遠處的 B 畫面也必須同步更新。

閱讀 3 分鐘
Swift 觀察者模式:從 NotificationCenter 到 Combine 封面圖

開發 iOS App 時,總會遇到這種情況:A 畫面發生變更,遠處的 B 畫面也必須同步更新。

如果硬是用代理程式把畫面彼此連起來,程式碼很容易像義大利麵一樣糾結。

這時就該使用觀察者模式。先說結論,在 Swift 中從 NotificationCenter 開始,再轉向 Combine,是最自然的流程。

本文會從觀察者模式的概念開始,介紹 NotificationCenter 的用法,以及現今常用的 Combine,並搭配實際程式碼說明。

什麼是觀察者模式?

觀察者模式是一種結構:當某個對象的狀態改變時,會自動通知正在監看的物件。

可以想像 YouTube 訂閱。頻道(發布者)上傳影片後,訂閱者(觀察者)就會收到通知。

重點在於發布者和觀察者不必直接知道彼此的存在。

發布者和觀察者互不知情,只交換通知
發布者和觀察者互不知情,只交換通知

頻道不在意有多少訂閱者,也不在意他們是誰,只要喊一聲「影片上架了」即可。

這種鬆散耦合能讓程式碼不易糾結,之後修改也方便許多。


NotificationCenter 怎麼使用?

NotificationCenter 是 Apple 內建的觀察者模式工具,不需額外的函式庫就能直接使用。

首先定義通知名稱,接著撰寫發布端的程式碼。

// 定義通知名稱
extension Notification.Name {
    static let didLogin = Notification.Name("didLogin")
}

// 登入成功時發布通知
NotificationCenter.default.post(name: .didLogin, object: nil)

接收端則可以這樣註冊觀察者。

// 訂閱通知
NotificationCenter.default.addObserver(
    self, selector: #selector(handleLogin),
    name: .didLogin, object: nil
)

有一點要注意:舊式方法(addObserver + selector)若在畫面消失時沒有呼叫 removeObserver,可能會產生問題。

不過 iOS 9 以上已有更多部分會自動清理;使用區塊式 API 時,只要留意管理回傳的權杖即可。


改用 Combine 有什麼好處?

Combine 是 Apple 在 2019 年發表的反應式程式設計框架,從 iOS 13 開始即可使用。

NotificationCenter 也能轉換成 Combine 的 Publisher,因此可以自然地和既有程式碼混用。

// NotificationCenter改成 Combine 方式
let token = NotificationCenter.default
    .publisher(for: .didLogin)
    .sink { _ in
        print("偵測登入!")
    }

Combine 真正的優點,在於可以將資料流串接起來。

可以使用 map、filter 等運算子加工值,也能合併多個事件。

將 sink 回傳的訂閱(AnyCancellable)存入變數後,變數消失時訂閱也會自動取消,記憶體管理輕鬆許多。

我會開著程式碼親手撰寫,藉此掌握使用感
我會開著程式碼親手撰寫,藉此掌握使用感

所以該用哪一個?

要看情況。以下用表格整理。

項目 NotificationCenter Combine
導入時機 較舊的內建 API iOS 13+
學習難度 中等以上
值加工 困難 使用 map/filter 彈性處理
記憶體管理 需手動處理 保存訂閱後自動處理

只有一兩個簡單通知時,NotificationCenter 就足夠了。

相反地,如果資料流複雜,或需要組合多個事件,Combine 會整潔得多。

如果是第一次接觸,先用 NotificationCenter 熟悉概念,習慣後再轉向 Combine。

當然,現在 SwiftUI 和 async/await 正夯,因此新專案也建議一併考慮這兩者。

一個通知跳出時,連其他畫面也一起更新,真的會很有成就感
一個通知跳出時,連其他畫面也一起更新,真的會很有成就感

總結

今天從 NotificationCenter 到 Combine,快速瀏覽了 Swift 的觀察者模式。

不用一開始就追求完全理解,先親手做出一個小通知吧。實際寫過之後,很快就能掌握訣竅。加油!

延伸閱讀