(追記) 正しい使い方は @mono0926 さんがこちらにて書いていますのでそちらへどうぞ
回れ右。
※以下の内容は一応アーカイブとして残しておきますが、良き使い方ではないのでご了承くださいませ。
Swift3.0で変わったNSNotificationCenter
Swift2からSwift3に変わって、Foundation系にも手が加えられ、 NSNotificationCenter
周りにも変更がありました。
Swift2.2とSwift3.0でaddObserveする場合の同じ処理を見比べてみます。
class Hoge {
@objc func doSomething(notification: Notification) {
// ...
}
}
NSNotificationCenter.defaultCenter().addObserver(
self,
selector: #selector(hoge.doSomething(_:)),
name: "NotificationKey",
object: nil
)
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からは通知を発行するためのname
はString
ではなくてこのNSNotification.Name
を使えとなったようです。
しかし、これ毎回
NSNotification.Name("SomeNotificationKey")
ってやるの しんどい ですよね。
なので、今までどおりの形に近い感じで使えるようにしてみます。
魔法をかける
できれば今までどおり name: "NotificationKey"
と書きたいので、 NSNotification.Name に魔法をかけます。
ここで、 ExpressibleByStringLiteral というprotocolの出番となります。
ちなみに、この ExpressibleByStringLiteral は、Swift2では StringLiteralConvertible という名前でした。
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を基に書いています。