Combine入門 | CombineでNotificationを受け取る方法

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でイベントを受け取るには次のようにします。

  1. Publisherを取得する。
  2. 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を使わないときは、次のようになります。

  1. NotificationCenterを取得する
  2. NotificationCenterにオブザーバーを登録する
  3. オブザーバーでNotificationを受け取って、処理を行う

Combineのときは、次のようになります。

  1. NotificationCenterからPublisherを取得する
  2. 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からの写真です

著書紹介

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

Akira Hayashi (林 晃)のアバター Akira Hayashi (林 晃) Representative(代表), Software Engineer(ソフトウェアエンジニア)

アールケー開発代表。Appleプラットフォーム向けの開発を専門としているソフトウェアエンジニア。ソフトウェアの受託開発、技術書執筆、技術指導・セミナー講師。note, Medium, LinkedIn
-
Representative of RK Kaihatsu. Software Engineer Specializing in Development for the Apple Platform. Specializing in contract software development, technical writing, and serving as a tech workshop lecturer. note, Medium, LinkedIn

目次