我想我已经想出了如何让NSNotifications 的处理更加优雅:
enum NotificationType: String {
case Foo
case Bar
}
enum Notification {
case Foo(Int)
case Bar
// the fragile part
var type: NotificationType {
let name = Mirror(reflecting: self).children.first.flatMap({ $0.label }) ?? "\(self)"
return NotificationType(rawValue: name)!
}
var asNSNotification: NSNotification {
let name = type.rawValue
let userInfo = [String: AnyObject]()
switch self {
case let .Foo(intVal):
userInfo["intVal": intVal]
default:
break
}
return NSNotification(name: name, object: nil, userInfo: userInfo)
}
init?(fromNSNotification n: NSNotification) {
let type = NotificationType(rawValue: n.name)!
switch type {
case .Bar:
self = .Bar
case .Foo:
let value = n.userInfo?["intVal"] ?? 0
self = .Foo(value)
default:
return nil
}
}
}
此解决方案依赖于Notification 和NotificationType 中的案例名称相同的约定。有人可能会说这样的设计很脆弱,但我认为与我们所取得的成果相比,这是一个小小的权衡。
下面是一个处理订阅/取消订阅的小助手类:
protocol NotificationReceiving: class {
func didReceiveNotification(notification: Notification)
}
class NotificationReceiver {
private weak var delegate: NotificationReceiving?
private(set) var subscriptions = Set<NotificationType>()
init(delegate: NotificationReceiving?) {
self.delegate = delegate
}
func subscribe(notificationTypes: NotificationType...) {
let nc = NSNotificationCenter.defaultCenter()
let doSubscribe: NotificationType -> Void = {
nc.addObserver(self, selector: "handleNotification:", name: $0.name, object: nil)
self.subscriptions.insert($0)
}
notificationTypes.forEach(doSubscribe)
}
func unsubscribe(notificationTypes: NotificationType...) {
let nc = NSNotificationCenter.defaultCenter()
if notificationTypes.isEmpty {
nc.removeObserver(self)
} else {
let doUnsubscribe: NotificationType -> Void = {
nc.removeObserver(self, name: $0.name, object: nil)
self.subscriptions.remove($0)
}
notificationTypes.forEach(doUnsubscribe)
}
}
@objc private func handleNotification(notification: NSNotification) {
if let n = Notification(fromNSNotification: notification) {
delegate?.didReceiveNotification(n)
}
}
}
这是客户的样子:
class Client: NotificationReceiving {
private var receiver: NotificationReceiver!
init() {
receiver = NotificationReceiver(delegate: self)
receiver.subscribe(.Foo, .Bar)
}
deinit {
receiver.unsubscribe()
}
func didReceiveNotification(notification: Notification) {
switch notification {
case let .Foo(val):
print("foo with val: \(val)")
// this notification is one-shot:
receiver.unsubscribe(.Foo)
case .Bar:
print("bar!")
}
}
}
不确定以一种方法处理所有通知是否是好的设计(我想当有很多通知时它可能会变得混乱),但关键是我们现在可以使用类型安全和模式匹配的强大功能,这太棒了。