[Foundation] Scale back struct Notification's userInfo to be in-line with NSNotification

This simplifies the bridging story for Notifications to their objc counterparts since the id -> Any and AnyHashable changes have now been applied (which makes the previous boxing strategy no longer needed). Previous consumers of Notification that were using String keys should still work, however any explicit dictionary types should migrate from Swift 2.2 -> Swift 3 from userInfo as [NSObject:AnyObject] to [AnyHashable:Any]. The condition of distributed notifications (in non sandboxed apps) requiring plist types still applies and will fail at runtime if incorrect types are passed into the objective-c layer, and in the case of sandboxed apps userInfo still is forbidden (this change is a non functional change in the respect to those behaviors).

Resolves the following issues:
<rdar://problem/27426757>
<rdar://problem/27561621>
<rdar://problem/27259984>
This commit is contained in:
Philippe Hausler
2016-08-03 11:11:05 -07:00
parent 43f39b095e
commit f96159b582
5 changed files with 22 additions and 297 deletions

View File

@@ -13,12 +13,6 @@
@_exported import Foundation // Clang module
@_silgen_name("__NSNotificationCreate")
internal func __NSNotificationCreate(_ name: NSString, _ object: AnyObject?, _ userInfo: AnyObject?) -> NSNotification
@_silgen_name("__NSNotificationUserInfo")
internal func __NSNotificationUserInfo(_ notif: NSNotification) -> AnyObject?
/**
`Notification` encapsulates information broadcast to observers via a `NotificationCenter`.
*/
@@ -34,12 +28,12 @@ public struct Notification : ReferenceConvertible, Equatable, Hashable {
public var object: Any?
/// Storage for values or objects related to this notification.
public var userInfo: [String : Any]?
public var userInfo: [AnyHashable : Any]?
/// Initialize a new `Notification`.
///
/// The default value for `userInfo` is nil.
public init(name: Name, object: Any? = nil, userInfo: [String : Any]? = nil) {
public init(name: Name, object: Any? = nil, userInfo: [AnyHashable : Any]? = nil) {
self.name = name
self.object = object
self.userInfo = userInfo
@@ -61,8 +55,6 @@ public struct Notification : ReferenceConvertible, Equatable, Hashable {
public typealias Name = NSNotification.Name
/// Compare two notifications for equality.
///
/// - note: Notifications that contain non NSObject values in userInfo will never compare as equal. This is because the type information is not preserved in the `userInfo` dictionary.
public static func ==(lhs: Notification, rhs: Notification) -> Bool {
if lhs.name.rawValue != rhs.name.rawValue {
return false
@@ -80,10 +72,8 @@ public struct Notification : ReferenceConvertible, Equatable, Hashable {
}
if let lhsUserInfo = lhs.userInfo {
if let rhsUserInfo = rhs.userInfo {
if lhsUserInfo.count != rhsUserInfo.count {
return false
}
return _NSUserInfoDictionary.compare(lhsUserInfo, rhsUserInfo)
// user info must be compared in the object form since the userInfo in swift is not comparable
return lhs._bridgeToObjectiveC() == rhs._bridgeToObjectiveC()
} else {
return false
}
@@ -116,11 +106,7 @@ extension Notification : _ObjectiveCBridgeable {
@_semantics("convertToObjectiveC")
public func _bridgeToObjectiveC() -> NSNotification {
if let info = userInfo {
return __NSNotificationCreate(name.rawValue as NSString, object.map { $0 as AnyObject }, _NSUserInfoDictionary.bridgeValue(from: info))
}
return NSNotification(name: name, object: object, userInfo: nil)
return NSNotification(name: name, object: object, userInfo: userInfo)
}
public static func _forceBridgeFromObjectiveC(_ x: NSNotification, result: inout Notification?) {
@@ -130,18 +116,7 @@ extension Notification : _ObjectiveCBridgeable {
}
public static func _conditionallyBridgeFromObjectiveC(_ x: NSNotification, result: inout Notification?) -> Bool {
if let userInfo = __NSNotificationUserInfo(x) {
if let info : [String : Any]? = _NSUserInfoDictionary.bridgeReference(from: userInfo) {
result = Notification(name: x.name, object: x.object, userInfo: info)
return true
} else {
result = nil
return false // something terrible went wrong...
}
} else {
result = Notification(name: x.name, object: x.object, userInfo: nil)
}
result = Notification(name: x.name, object: x.object, userInfo: x.userInfo)
return true
}