NotifwiftでNSNotification.userInfoをSwiftyに扱おう

（追記： potatotips27でNotifwiftについて発表しました。スライドはこちら）
https://speakerdeck.com/takasek/nsnotification-dot-userinfowo-swiftynixi-ou


# NSNotificationCenter。使いにくくありませんか？

NSNotificationCenterは、そこかしこで微妙に面倒な取り扱いを強いられます。

## observerの管理が面倒

`func addObserver(observer: AnyObject, selector aSelector: Selector, name aName: String?, object anObject: AnyObject?)` を使うと、selectorをリテラルで指定しなければなりません。コンパイラでselectorの存在を保証してくれないのは嫌ですね。
`func addObserverForName(name: String?, object obj: AnyObject?, queue: NSOperationQueue?, usingBlock block: (NSNotification) -> Void) -> NSObjectProtocol` を使えばナウいblockでの実装が可能ですが、removeObserverのためには戻り値をどこかに保持しておく必要があります。えー？　そのためにArrayを作れって…？
そしてどちらにせよ、observeを止めるためにはremoveObserverを明示的に呼ぶ必要があります。removeし忘れて煮え湯を飲まされた苦い思い出、誰にでもありますよね。

## userInfoが使いにくい

Notificationの付帯情報として指定できるuserInfo、型はなんと `[String: AnyObject]`。keyの整合性に気をつけないといけないわ、StructやEnumを渡せないわ、定義を見てるだけで悲しくなります。
Objective-Cとの互換性のため仕方ないこととは言え、Swiftで書くならもっと静的型付けを活用したいものですよね。

## 記述が長い

`NSNotificationCenter.defaultCenter().postNotificationName(SomeNotificationName, object:nil, userInfo: ["hogehoge": hoge])`
`NSNotificationCenter.defaultCenter().addObserver(self, selector: "someNotificationReceived:", name: SomeNotificationName, object: self)`
…どんだけ長ったらしく書かせるんだ！
一文字タイプするごとにHPの削られる音が聞こえます。

# というわけで、ライブラリ作りました。

https://github.com/takasek/Notifwift

## 使い方（例）

```swift
let didReceiveUserNotification = "didReceiveUserNotification"

final class MyViewController: UIViewController {
    @IBOutlet var userInfoView: MyUserInfoView!
    let notifwift = Notifwift()

    override func viewDidLoad() {
        super.viewDidLoad()
        notifwift.observe(didReceiveUserNotification, block: reload)
    }

    private func reload(user: User) {
        userInfoView.reload(user)
    }
}

final class MyUserRepository {
    func fetchUser(id: Int) {
        MyAPIManager.fetchUser(id) { (user: User) in
            Notifwift.post(didReceiveUserNotification, payload: user)
        }
    }
}
```

`Notifwift()` でインスタンスを生成すると、**そのインスタンスが生きている間だけ**NSNotificationをobserveします。
もうremoveObserver忘れに悩まされる心配はありません。
NSNotificationのpost時には、payloadとしてUserをダイレクトに渡せます。クロージャで受けてもいいですし、Userを引数で受けるメソッドがあればそのままレシーバとして指定できますね。

## 詳細な使い方

```swift
    do {
        let nt = Notifwift()
        nt.observe(notificationName) { notification in
            print("Notifwift can observe NSNotification in simple way.", notification)
        }
        Notifwift.post(notificationName)
        //printed:
        // Notifwift can observe NSNotification in simple way. NSConcreteNotification 0x7fdfa0414b20 {name = Hoge}
    }
    
    Notifwift.post(notificationName)
    //printed nothing. Observers expire when the Notifwift instance(in this case) is destructed.
```

Notifwiftの内部でuserInfoをラップ/アンラップされるので、ユーザーは型にとらわれない `payload` を受け渡しに利用できます。payloadはジェネリクス型をサポート。

```swift
    let nt = Notifwift()
    nt.observe(notificationName) { (payload:String) in
        print("This closure observes nothing but NSNotification with String payload.", payload)
    }
    nt.observe(notificationName) { (payload:Int) in
        print("This closure observes nothing but NSNotification with Int payload.", payload)
    }
    
    Notifwift.post(notificationName, payload:"aaaa")
    //printed:
    // This closure observes nothing but NSNotification with String payload. aaaa
    
    Notifwift.post(notificationName, payload:1)
    //printed:
    // This closure observes nothing but NSNotification with Int payload. 1
```

もちろんサブタイプにも対応。

```swift
    class Animal {}
    class Cat: Animal {}
    
    let nt = Notifwift()
    nt.observe(notificationName) { (p:Animal) in
        print("Received Animal.", p)
    }
    nt.observe(notificationName) { (p:Cat) in
        print("Received Cat. Yes, of course, Notifwift recognizes subtypes.", p)
    }
    
    Notifwift.post(notificationName, payload:Animal())
    //printed:
    // Received Animal. (Animal #1)
    
    Notifwift.post(notificationName, payload:Cat())
    //printed:
    // Received Animal. (Cat #1)
    // Received Cat. Yes, of course, Notifwift recognizes subtypes. (Cat #1)
```

値付きEnumとの相性は抜群！

```swift
    enum SomeResult {
        case Success(String)
        case Fail(NSError)
    }
    let nt = Notifwift()
    nt.observe(notificationName) { (p:SomeResult) in
        switch p {
        case .Success(let str):
            print("Any Type can be used as a payload", str)
        case .Fail(let err) where err.code == 403:
            print("not authorized")
        case .Fail(let err) where err.code == 404:
            print("not found")
        case .Fail(let err):
            print("Notifwift has a chemistry with Enum Associated Values.", err)
        }
    }
    
    Notifwift.post(notificationName, payload:SomeResult.Success("like this."))
    //printed:
    // Any Type can be used as a payload like this.

    Notifwift.post(notificationName, payload:SomeResult.Fail(NSError(domain: "", code: 0, userInfo: nil)))
    //printed:
    // Notifwift has a chemistry with Enum Associated Values. Error Domain= Code=0 "(null)"
```

NSNotificationのobject, queueもそのまま使えます。

```swift
    let obj1 = NSObject()
    let obj2 = NSObject()
    let nt = Notifwift()
    nt.observe(notificationName) { _ in
        print("Received from all objects")
    }
    nt.observe(notificationName, from: obj1) { _ in
        print("Received from obj1 only")
    }
    nt.observe(notificationName, from: obj2) { _ in
        print("Received from obj2 only")
    }
    
    Notifwift.post(notificationName, from: obj1)
    //printed:
    // Received from all objects
    // Received from obj1 only
    
    Notifwift.post(notificationName, from: obj2)
    //printed:
    // Received from all objects
    // Received from obj2 only
```

## インストール方法

Cocoapods, Carthageに対応してます。(2015/2/12, Carthageで問題なく動くこと確認できました。 @su_k ありがとうございました！)

```ruby
pod "Notifwift"
```

```ruby
github "takasek/Notifwift"
```