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からの写真です

投稿者プロフィール

林 晃
林 晃macOS/iOSアプリ/SDK/ミドルウェア開発が専門の開発者
アールケー開発代表。macOS/iOSアプリ/SDK/ミドルウェア開発が専門の開発者。ObjC/Swift/C++使い。豊富な開発実務経験を基に、教育コンテンツ開発、技術書執筆、技術指導、技術セミナー講師、企業内研修講師、行政・自治体職員研修講師も行います。

基礎から学ぶMetal


「基礎から学ぶMetal」を執筆しました。本書はMetalを使ってGPUプログラミングを行うための最初のステップを解説するMetalの解説書です。

私が初めてGPUプログラミングを行ったとき、どこから手をつけて、学んでいけば良いのか分からず呆然としました。もし、あのとき、これを教えてくれればという部分を解説しました。本書で解説している部分はMetalの基礎となる部分で、Metalを使うときに必ず触れることになる部分です。

詳細

基礎から学ぶSwiftUI


「基礎から学ぶ SwiftUI」というタイトルの本を執筆しました。

SwiftUIの入門書です。

SwiftUIのコンセプトは「ユーザーインターフェイスを作るための最短パスを提供する」「一度学べば(Appleのプラットフォームの)どこにでも適用できる」です。

SwiftUIの概要から始まって、一つ一つのテクノロジートピックに注目しながらSwiftUIとは何か?どんなことができるのか?どのようなコードを書けば良いのかなどを丸々一冊使って解説しています。

詳細

関連記事

  1. M1 Mac でのドライバの読み込み判断フロー

  2. Combine入門 | CombineでTimer処理を行う方法

  3. SwiftでCのポインタのインクリメント・デクリメントのコードを書き換…

  4. WKWebViewとSFSafariViewControllerのどち…

  5. SwiftのUnsafeMutableBufferは[]演算子よりも速…

  6. コマンドライン引数でモックに差し替える

最近の著書

  1. 基礎から学ぶ SwiftUI

人気記事

最近の記事