LoginSignup
13
13

More than 5 years have passed since last update.

EventCenter: AndroidのEventBusのSwift版っぽいのを作りました

Last updated at Posted at 2015-08-27

はじめに

Swiftで普通にObserverパターンを作ろうとすると意外に面倒じゃないでしょうか。
KVOは何かやりたいことと違うし、NSNotificationCenterはTypeSafeじゃないし、通知名の文字列を管理するのも少し面倒です。

その点AndroidのEventBusは少し動的な要素が入りますが(Methodが使われていないとWarningが出るのを抑えないといけない)、なかなかバランスが良いと感じていて好きです。
Swiftでも同じ感じで作れないかなぁと試行錯誤していたら、まあまあ良い感じで作れたのでご紹介します。

SwiftEventBusという名前のpodが既にあったので、 EventCenter と名づけました。

EventCenterの良い所

  • 任意のObject(class/struct/enum等)をEventオブジェクトとしてpostできる
  • Event通知をもらう側がCastしなくても良い
  • Eventの種別は「class/struct/enum 等の型」で決まるので、Event名の文字列を管理しなくて良い(うっかり重複することがない)
  • どのThreadでEvent通知を欲しいかは、Event通知をもらう側で決められる

制限

  • swift1.2で動作確認しています

使い方

Install

cocoapods

  pod "EventCenter"

マニュアル Install

実体は1ファイルなので、
https://github.com/mokemokechicken/EventCenter/blob/master/Pod/Classes/EventCenter.swift
をそのままコピっても良いかもしれません。

使いかた

例えば、こんな感じです。

class ViewController: UIViewController {

    override func viewWillAppear(animated: Bool) {
        let ec = EventCenter.defaultCenter

        // Event HandlerとしてClosureを登録する。この引数の型にマッチしたHandlerのみが呼ばれる
        ec.register(self) { (event: MyAwesomeModel.UpdateEvent) in
            self.updateView()
        }

        ec.register(self) { (event: MyAwesomeModel.StoreEvent) in
            switch(event) {
            case .SUCCESS:
                print("store ok!")
            case .ERROR:
                print("store error!")
            }
        }

        // または、methodを登録することもできます。型がマッチしたEventの場合呼ばれます。
        ec.register(self, handler: self.onEvent)

        // 呼び出されるThreadを指定することもできます。MainThread用には便利Methodが用意されています。
        ec.registerOnMainThread(self, handler: onEvent)
    }

    override func viewWillDisappear(animated: Bool) {
        // registerの第一引数に指定したObjectのhandler全てを解除します。
        EventCenter.defaultCenter.unregister(self)
    }

    func updateView() {
        // ...
    }

    func onEvent(event: MyAwesomeModel.UpdateEvent) {
        self.updateView()
    }

}


class MyAwesomeModel {
    class UpdateEvent {}
    enum StoreEvent {
        case SUCCESS
        case ERROR
    }

    func notifyUpdate() {
        EventCenter.defaultCenter.post(UpdateEvent())
    }

    func notifyStoreResult() {
        EventCenter.defaultCenter.post(StoreEvent.SUCCESS)
    }
}

// If you want to see more cases, see also Tests.swift

Source

ポイント

let h = hander as? (T -> Void)

みたいな感じで、マッチする handler を簡単に選択できるということに気がついたので、
勢いで作ったという感じです。

EventCenter.swift
    public func post<T>(obj: T) {
        for info in observers {
            if let h = info.handler as? (T -> Void) {
                if let queue = info.queue {
                    dispatch_async(queue) { h(obj) }
                } else {
                    h(obj)
                }
            }
        }
    }

おわりに

もう少し便利メソッドなど増やしても良いかもしれません。
不具合などあれば教えていただけると幸いです。。

13
13
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
13
13