Combineはアップル純正の非同期処理を実装するためのフレームワークです。SwiftUIのバインディングなどでも使われています。重要なフレームワークです。
動作環境
Combineは次のような環境で動作します。
サポートしているOS
CombineがサポートしているOSは以下の通りです。
- iOS 13以降
- macOS Catalina 10.15以降
- tvOS 13以降
- watchOS 6以降
- Mac Catalyst 13以降
サポートしているプログラミング言語
CombineはSwift専用です。Objective-CやC言語からは使用できません。Swift専用フレームワークがOS標準フレームワークに登場です。
PublisherとSubscriber
Combineでイベントを受け取るには次のようにします。
- Publisherを取得する。
- PublisherにSubscriberを接続する。
ここのセクションで書いた説明だと、やっぱり分からないという場合には、さらっと流して、次の「Notificationを受け取る」や「Timerと組み合わせる」の例を実行してみてください。それから、改めて読んで頂くと分かりやすいです。私はその順序で理解しました。
Publisherというのはイベントを発行するオブジェクトです。Publisherはプロトコルです。イベントを発行するオブジェクトをから取得できます。
SubscriberはPublisherが発行したイベントを受け取るオブジェクトです。Subscriberもプロトコルです。Combineには次の2つのSubscriberがビルトインされています。
- クロージャーを実行するSubscriber
- 変更後の値をプロパティに設定するSubscriber
それぞれ、Publisherの次のメソッドで取得できます。
// イベントを受け取ったときに、Completionを実行する
// 値を受け取ったときに、receiveValueを実行する
func sink(receiveCompletion: @escaping ((Subscribers.Completion<Failure>) -> Void),
receiveValue: @escaping ((Output) -> Void)) -> AnyCancellable
// 値を受け取ったときに、receiveValueを実行する
func sink(receiveValue: ((Output) -> Void)) -> AnyCancellable
// keyPathに指定したプロパティに変更後の値を設定する
func assign<Root>(to keyPath: ReferenceWritableKeyPath<Root, Self.Output>, on object: Root) -> AnyCancellable
それぞれのメソッドが返したAnyCancellableに適合するオブジェクトは、イベントを受け取りたい間は、どこかのプロパティに入れて保持します。
必要なくなったら、cancelメソッドを呼ぶと、イベントを受け取らなくなります。また、プロパティを持っているオブジェクトが破棄されて、AnyCacellableのdeinitが呼ばれても受け取らなくなります。
Notificationを受け取る
Notificationを受け取るには、Combineを使わないときは、次のようになります。
- NotificationCenterを取得する
- NotificationCenterにオブザーバーを登録する
- オブザーバーでNotificationを受け取って、処理を行う
Combineのときは、次のようになります。
- NotificationCenterからPublisherを取得する
- sinkを使ってNotification受信時の処理を設定する
sinkで設定するクロージャーの引数「Output」はPublisherが定義します。NotificationCenterのPublisherのOutputは次のように定義されていて、Notificationになっています。
typealias NotificationCenter.Publisher.Output = Notification
Combineを使ってNotificationを受け取る
次のコードはCombineを使ってNotificationを受け取っている例です。
import UIKit
import Combine
class ViewController: UIViewController {
@IBOutlet var textField: UITextField!
var didChangeCancellable: AnyCancellable?
override func viewDidLoad() {
super.viewDidLoad()
didChangeCancellable = NotificationCenter.default
.publisher(for: UITextField.textDidChangeNotification, object: textField)
.sink(receiveValue: { (notification) in
print(notification.name.rawValue)
if let field = notification.object as? UITextField {
if let text = field.text {
print("New Text: \(text)")
}
}
})
}
}
publisherメソッドの引数で、受信したいNotificationとオブジェクトを指定します。Notificationだけを指定したい場合は、引数 objectを省略します。
この辺の考え方は、Combineを使わずに、addObserverメソッドを使ったときと同じですね。
Combineを使わなかったときのコード
次のコードはサンプルコードと同じ処理を、Combineを使わずに実装したコードです。
import UIKit
import Combine
class ViewController: UIViewController {
@IBOutlet var textField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default
.addObserver(self, selector: #selector(textDidChangeNotification(_:)), name: UITextField.textDidChangeNotification, object: textField)
}
@objc func textDidChangeNotification(_ notification: Notification) {
print(notification.name.rawValue)
if let field = notification.object as? UITextField {
if let text = field.text {
print("New Text: \(text)")
}
}
}
}
違いは、次の2つです。
- 登録するのはオブザーバーオブジェクト
- 呼び出す処理は@objc付きのメソッド
Notificationを受信したときの処理自体には違いがありません。このことから、既存の処理をCombineに変更することや、Notificationを投げるクラスを使うときに、Combineを組み合わせることは簡単にできることが分かります。
先頭のアイキャッチ画像はhimawariinさんによる写真ACからの写真です