【iOS】図解解説！NotificationCenter

NotificationCenter、わかったつもりでいざ使おうとしたら全然わかってなかったので、記事書きます。

# 対象
- iOS開発初心者
- NotificationCenterが何かわかってない人
- NotificationCenterの名前は知っているが、イマイチ理解し切れていない人

# 前提として
コードに入る前に、NotificationCenterに関わる登場人物を整理させてください。

## なぜNotificationCenterか
NotificationCenterはイベント通知の仕組みです。

![名称未設定.002.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/333454/3acdfc13-cf44-8268-1812-e99c3d546eb8.jpeg)

登場人物はObserverとSubjectという二つのクラスです。
Subjectはイベントの発生を通知するクラスです。
Subject内のイベント発生タイミングで処理を完結させられたら、イベントを通知する必要はありません。
しかし別クラスでないとできない処理が、そのイベントを知りたいときがありますね。

単純にやるのであれば、ObserverクラスとSubjectクラスで互いにアクセス可能にしてやる、という方法が思いつきます。
これでも実装は可能は可能なのですが、両方のクラスが密結合になってしまう問題が発生します。
役割の全然違うクラスが、あるイベントを通じて結びついてしまうのは避けたいですね……😵😵😵

![名称未設定.003.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/333454/879613da-3faf-0dcf-4187-73278a503415.jpeg)

そこでNotificationCenterを使います！

## NotificationCenterのお仕事は仲介
 ![名称未設定.004.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/333454/7b64a0fd-f6fc-356c-61c3-28c1b6f2f586.jpeg)
NotificationCenterを使って、イベント通知の実装をすると、上の図の流れになります。
「ユーザーがアプリを終了させようとした」というイベントを例とします。

① (Observer)アプリを終了させるイベント時に動かしたい処理をNotificationCenterに教える
② (Subject)アプリ終了のイベントを通知
③ ①で登録した処理が動く

※このイベントは、[willTerminateNotification](https://developer.apple.com/documentation/uikit/uiapplication/1623061-willterminatenotification)という名前で、UIApplicationDelegateの中に定義されています。
本記事の中では詳しく説明しません。

Observer/Subjectは、NotificationCenterを通じて疎結合が保たれています。

## コードを読む前に
![名称未設定.005.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/333454/3f4db2a9-08f5-ed17-c4a7-51bda0ac914c.jpeg)
あとは上記の①〜③をコードで書くだけなのですが、コードを読む前にもう少し補足情報があります。

- NotificationCenterは自分でオリジナルを定義することもできるが、必要がなければdefaultを使う
    - というかdefualtのNotificationCenterじゃ困るケースってそうそうないと思う
    - 使い道はあまりないものの、一応できるようにした系？
- ObserverはaddObserver()というメソッドを使って、自身を登録する
    - Subjectは登録不要。NotificationCenterのpost()メソッドを使ってイベント通知する
    - この順序関係がずっと本の解説読んでもわかりづらいなと思ってたんですが、イベント通知する処理の前にイベント監視する奴を登録していて、
    - しかもイベント通知の後に登録した奴のメソッドが動くので、
    - 頭の中で整理できていなかったため、処理の順序がごちゃごちゃになって混乱していたみたいです

# コード例
念入りに前提を説明してきましたが、いよいよコードに入りましょう。
「Event Occurs」というイベント名にしています。

```swift
import Foundation

class Subject {
    let eventName: Notification.Name
    
    init(eventName: Notification.Name) {
        self.eventName = eventName
    }
    
    func post() {
        NotificationCenter.default.post(name: eventName, object: nil)
    }
}

class Observer {
    let eventName: Notification.Name
    
    init(eventName: Notification.Name) {
        self.eventName = eventName
        NotificationCenter.default.addObserver(self, selector: #selector(doWhenEventOccur(_:)), name: eventName, object: nil)
    }
    
    @objc func doWhenEventOccur(_ notification: Notification) {
        print("イベント発生時に動かしたい処理を記述")
    }
}

//クラスをインスタンス化している箇所
let eventName = Notification.Name("Event Occurs")
let observer = Observer(eventName: eventName)
let subject = Subject(eventName: eventName)

//イベントが発生した
subject.post() // イベント発生時に動かしたい処理を記述
```

# NotificationCenterを使うメリット/デメリット
上記の例はあくまで解説のためのコードなので、メリットがわかりづらいですね。
メリデメについて説明します。

## メリット
疎結合を保ったまま１対多、または多対多のイベント通知が実装できるのが最大のメリットです。
![名称未設定2.001.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/333454/a29ec124-67be-154a-342e-80b737dd6aab.jpeg)
たとえばですが、イベントが発生するクラスが今後増加したとしても、
NotificationCenterを使っていたとしたら、Observerサイドの修正は不要です。

もしObserverが直接監視対象のクラスの参照を持っていたら、こうはいきませんね。
また、iOS開発ではDelegateやクロージャもイベント通知でよく使われますが、
両方とも1対1のイベント通知なので、上図のような関係性では使えません。

## デメリット
疎結合を保ったまま多対多のイベント通知が可能というメリットは、
裏を返せばどのクラスからもイベントがpost可能で予想できない、というデメリットでもあります。
濫用するとスパゲッティコード化するので気をつけましょう。

# 参考
[Swift実践入門](https://amzn.to/300BJkt)
[Swift 3 以降の NotificationCenter の正しい使い方](https://qiita.com/mono0926/items/754c5d2dbe431542c75e)
[Swiftとオブジェクト間の通知のパターン](https://qiita.com/shtnkgm/items/f07f1c61985214ceb7b3)

# 追記
セレクタについての続編を書きましたので、ご興味あれば。

[Selector完全攻略、そして初学者特有のAddTarget()やAddObserver()のセレクタに変数を渡そうとする願望について](https://qiita.com/eg_19_/items/43c5c0f5e1f727ba0ff0)