0
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

【SwiftChaining】 NotificationCenterからの通知を受け取る

Last updated at Posted at 2019-03-02

SwiftChainingの解説記事その3です。

SwiftChainingとは何か?というのは、こちらの最初の記事を参考にしてください。

NotificationCenterからの通知を受け取る

単に値を送信するクラスとしてNotifierがあるというのは最初の記事に書きましたが、自分で何かを通知するならそれで良いとして、NotifierNotificationCenterの通知には対応していません。

NotificationCenterのイベントをSwiftChainingで扱えるようにするために、NotificationAdapterというクラスを別に用意しています。コード例としては以下のようになります。

import UIKit
import Chaining

class MyClass {
    static let name = Notification.Name("MyClassNotificationName")
    
    func post(value: String) {
        NotificationCenter.default.post(name: MyClass.name,
                                        object: self,
                                        userInfo: ["key": value])
    }
}

let object = MyClass()

let adapter = NotificationAdapter(MyClass.name,
                                  object: object,
                                  notificationCenter: .default)

let observer = adapter.chain()
    .do { (notification: Notification) in
        // userInfo["key"]の値をログに出力
        print(notification.userInfo!["key"]!)
    }
    .end()

// userInfo["key"]に"test"を入れて通知を送り、"test"がログに出力される
object.post(value: "test")

解説

NotificationAdapterは、NotificationCenterから通知されたイベントをSwiftChainingのイベントに変換するクラスです。以下のように生成します。

let adapter = NotificationAdapter(MyClass.name,
                                  object: object,
                                  notificationCenter: .default)

ひとつめの引数には通知を受け取りたいNotification.Nameを渡し、2つめのobjectには送信元のインスタンスを渡します。この辺りはNotificationCenteraddObserverを呼ぶ場合と同じ意味です。3つめにはNotificationCenterを渡せますが、通常は.defaultで良いでしょう。

なお、objectnotificationCenterは省略できます。省略すると、objectnilになりどのオブジェクトからも関係なく受け取りますし、notificationCenterを省略したら.defaultになります。

NotificationAdapterにもchain()関数があってバインディング処理が書けますので、通知を受け取った時の処理はNotifierと同じように書けます。

let observer = adapter.chain()
    .do { (notification: Notification) in
        print(notification.userInfo!["key"]!)
    }
    .end()

イベントで送られてくる値の型はNotificationですので、上記のコードのようにdoで受け取れば、普通にaddObserverした時と同じような感じにクロージャで処理が書けます。

とはいえdoを使うだけだと敢えてライブラリを使っている意味がないので、もう少しちゃんとバインディングする感じの使い方を紹介します。

あくまでuserInfoに必要な値が入っていると言う前提ですが、以下のようにmapuserInfoからStringの値を取り出してイベントの型を変換し、sendToで別のオブジェクトが受け取る感じの書き方ができます。

// 受け取るオブジェクト
let receiver = ValueHolder<String>("")

let observer = adapter.chain()
    .map { (notification: Notification) -> String? in
        return notification.userInfo?["key"] as? String
    }
    .sendTo(receiver)
    .end()

object.post(value: "test")

// receiverの値は"test"になっている
print(receiver.value)

sendToが受け取れる型

ちなみに、このコードを見て違和感を感じた方がいるかもしれません。値を受け取るValueHolderの型はStringなのに、mapで変換して送る値の型はオプショナルのString?で型が違っています。

sendTo関数はオプショナルの有無の違いであれば、値を送れるようにしています。今回のコードのようにオプショナルから非オプショナルの場合だと、nilが来たら無視するようになっています。

Adapterの保持

上記のサンプルコードではNotificationAdapterlet adapterでローカルで保持しているだけなのですが、実際のアプリでは監視が必要な間はどこかしらのプロパティに保持する必要があります。NotificatoinAdapterが破棄されてしまうとNotificationCenterの監視が外れてしまうからです。

そのあたりを意識せずに書いていると、インスタンスが作られたそばから解放されてて動いていないという事が起こってしまいます。

とは言えAdapterに対して後から操作する訳でもないのにプロパティを作るのも冗長な感じがするので、解決法を用意しています。それはまた別の記事で紹介します。

0
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?