我想将enum与关联值一起用于类型安全NSNotification
s:
enum Notification {
case Foo(Int)
case Bar
var rawValue: String {
switch self {
case .Foo:
return "Foo"
case .Bar:
return "Bar"
}
}
var asNSNotification: NSNotification {
let userInfo = [String: AnyObject]()
switch self {
case let .Foo(intVal):
userInfo["intVal": intVal]
default:
break
}
return NSNotification(name: rawValue, object: nil, userInfo: userInfo)
}
init?(fromNSNotification n: NSNotification) {
switch n.name {
case .Bar:
self = .Bar
case .Foo(42): // some bogus value
let realValue = n.userInfo?["intVal"] ?? 0
self = .Foo(realValue)
default:
return nil
}
}
}
这应该可行,但肯定是一段丑陋的代码。任何人都有想法如何让它更优雅?
编辑:我想使用枚举的原因是使每个通知的参数类型安全。
更优雅"我的意思是:
rawValue
属性(避免switch
)好的,这里有rawValue
属性如何简化:
var rawValue: String {
return Mirror(reflecting: self).children.first.flatMap({ $0.label }) ?? "\(self)"
}
答案 0 :(得分:3)
在我看来,你的枚举工作太辛苦了。这应该足够了:
enum Notification {
case Foo(Int)
case Bar
func notification() -> NSNotification {
switch self {
case Foo(let intVal):
return NSNotification(name: "Foo", object: nil, userInfo: ["IntVal":intVal])
case Bar:
return NSNotification(name: "Bar", object: nil)
}
}
}
添加提供非零object
的能力留给读者练习。
答案 1 :(得分:2)
我想我已经想出如何使NSNotification
的处理更加优雅:
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!")
}
}
}
不确定在一种方法中处理所有通知都是好的设计(我想在有很多通知的情况下它可能会变得混乱),但重点是我们现在可以使用类型安全和模式匹配的强大功能,这很棒。