LoginSignup
40
35

More than 5 years have passed since last update.

Swift3のNotificationCenterを今までと近い形で使ってみる

Last updated at Posted at 2016-08-31

(追記) 正しい使い方は @mono0926 さんがこちらにて書いていますのでそちらへどうぞ :runner:

回れ右。

※以下の内容は一応アーカイブとして残しておきますが、良き使い方ではないのでご了承くださいませ。




Swift3.0で変わったNSNotificationCenter

Swift2からSwift3に変わって、Foundation系にも手が加えられ、 NSNotificationCenter 周りにも変更がありました。

Swift2.2とSwift3.0でaddObserveする場合の同じ処理を見比べてみます。

Swift2
class Hoge {
    @objc func doSomething(notification: Notification) {
        // ...
    }
}

NSNotificationCenter.defaultCenter().addObserver(
    self, 
    selector: #selector(hoge.doSomething(_:)), 
    name: "NotificationKey", 
    object: nil
)

Swift3
class Hoge {
    @objc func doSomething(_ notification: Notification) {
        // ...
    }
}

let hoge = Hoge()
NotificationCenter.default.addObserver(
    hoge, 
    selector: #selector(hoge.doSomething(_:)), 
    name: NSNotification.Name("NotificationKey"),  //おや...?
    object: nil
)

ここで、 NSNotification.Name という見慣れないものがでてきました。
(ちなみに Notification.Name ものもあって、辿ると NSNotification.Name のエイリアスになっているのでほぼ同義です。どちらで書いても間違いではなさそう。)

どうやらSwift3からは通知を発行するためのnameStringではなくてこのNSNotification.Nameを使えとなったようです。
しかし、これ毎回

NSNotification.Name("SomeNotificationKey")

ってやるの しんどい ですよね。
なので、今までどおりの形に近い感じで使えるようにしてみます。


魔法をかける

できれば今までどおり name: "NotificationKey" と書きたいので、 NSNotification.Name に魔法をかけます。
ここで、 ExpressibleByStringLiteral というprotocolの出番となります。
ちなみに、この ExpressibleByStringLiteral は、Swift2では StringLiteralConvertible という名前でした。

ExpressibleByStringLiteralをNotification.Nameに適応させる
extension NSNotification.Name: ExpressibleByStringLiteral {
    public init(unicodeScalarLiteral value: String) {
        self.init(rawValue: value)
    }

    public init(extendedGraphemeClusterLiteral value: String) {
        self.init(rawValue: value)
    }

    public init(stringLiteral value: String) {
        self.init(rawValue: value)
    }
}

こうすることで、

// good!
NotificationCenter.default.addObserver(
    hoge, 
    selector: #selector(hoge.doSomething(_:)), 
    name: "NotificationKey", 
    object: nil
)

このように、文字リテラル("")を使って今までどおりの書き方ができるようになります。

...え、これが 変数の場合はどうするの? ってなりますよね。
その場合は、変数を定義する型を Notification.Name にします。


class Hoge {
    // 左辺側で型を指定してあげることで、右辺はStringではなくて、Notification.Nameと推論される
    static let NotificationKey: NSNotification.Name = "NotificationKey"

    // NG : 今までどおりの宣言だと、Stringと推論される
    // static let NotificationKey = "NotificationKey"

    @objc func doSomething(_ notification: Notification) {
        // ...
    }
}

// good!
NotificationCenter.default.addObserver(
    hoge, 
    selector: #selector(hoge.doSomething(_:)), 
    name: Hoge.NotificationKey,  // 今までどおりいける!
    object: nil
)

まとめ

Swift3になって NSNotification.Name とか出てきてちょっと怯んだのですが、少し工夫してあげると扱いやすくなるかもしれません。 ExpressibleByStringLiteral ありがとう。

おまけ

そこそこ利用頻度の高い、UIApplicationDidBecomeActiveNotification といったUIApplication周りのNotificationのkeyに関しては、

// swift2まではこれ
UIApplicationDidBecomeActiveNotification
// swift3からはこれ
NSNotification.Name.UIApplicationDidBecomeActive

のように変わったので気をつけましょう。もしかしたら自動マイグレーションなり補完が効くかもしれませんが。。。
(最初どこに行ってしまったのか気づけなかった...)

(※注 Xcode8 beta6 時点のSwift3.0を基に書いています。

40
35
3

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
40
35