// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // //===----------------------------------------------------------------------===// // // RUN: %target-run-simple-swift // REQUIRES: executable_test // REQUIRES: objc_interop import Foundation #if FOUNDATION_XCTEST import XCTest class TestUserInfoSuper : XCTestCase { } #else import StdlibUnittest class TestUserInfoSuper : NSObject { } #endif struct SubStruct: Equatable { var i: Int var str: String static func ==(lhs: SubStruct, rhs: SubStruct) -> Bool { return lhs.i == rhs.i && lhs.str == rhs.str } } struct SomeStructure: Hashable { var i: Int var str: String var sub: SubStruct static func ==(lhs: SomeStructure, rhs: SomeStructure) -> Bool { return lhs.i == rhs.i && lhs.str == rhs.str && lhs.sub == rhs.sub } // FIXME: we don't care about this, but Any only finds == on Hashables func hash(into hasher: inout Hasher) { hasher.combine(i) hasher.combine(str) hasher.combine(sub.i) hasher.combine(sub.str) } } /* Notification and potentially other structures require a representation of a userInfo dictionary. The Objective-C counterparts are represented via NSDictionary which can only store a hashable key (actually NSObject *) and a value of AnyObject (actually NSObject *). However it is desired in swift to store Any in the value. These structure expositions in swift have an adapter that allows them to pass a specialized NSDictionary subclass to the Objective-C layer that can round trip the stored Any types back out into Swift. In this case NSNotification -> Notification bridging is suitable to verify that behavior. */ class TestUserInfo : TestUserInfoSuper { var posted: Notification? func validate(_ testStructure: SomeStructure, _ value: SomeStructure) { expectEqual(testStructure.i, value.i) expectEqual(testStructure.str, value.str) expectEqual(testStructure.sub.i, value.sub.i) expectEqual(testStructure.sub.str, value.sub.str) } func test_userInfoPost() { let userInfoKey = "userInfoKey" let notifName = Notification.Name(rawValue: "TestSwiftNotification") let testStructure = SomeStructure(i: 5, str: "10", sub: SubStruct(i: 6, str: "11")) let info: [AnyHashable : Any] = [ AnyHashable(userInfoKey) : testStructure ] let note = Notification(name: notifName, userInfo: info) expectNotNil(note.userInfo) let nc = NotificationCenter.default nc.addObserver(self, selector: #selector(TestUserInfo.notification(_:)), name: notifName, object: nil) nc.post(note) expectNotNil(posted) if let notification = posted { let postedInfo = notification.userInfo expectNotNil(postedInfo) if let userInfo = postedInfo { let postedValue = userInfo[AnyHashable(userInfoKey)] as? SomeStructure expectNotNil(postedValue) if let value = postedValue { validate(testStructure, value) } } } } func test_equality() { let userInfoKey = "userInfoKey" let notifName = Notification.Name(rawValue: "TestSwiftNotification") let testStructure = SomeStructure(i: 5, str: "10", sub: SubStruct(i: 6, str: "11")) let testStructure2 = SomeStructure(i: 6, str: "10", sub: SubStruct(i: 6, str: "11")) let info1: [AnyHashable : Any] = [ AnyHashable(userInfoKey) : testStructure ] let info2: [AnyHashable : Any] = [ AnyHashable(userInfoKey) : "this can convert" ] let info3: [AnyHashable : Any] = [ AnyHashable(userInfoKey) : testStructure2 ] let note1 = Notification(name: notifName, userInfo: info1) let note2 = Notification(name: notifName, userInfo: info1) expectEqual(note1, note2) let note3 = Notification(name: notifName, userInfo: info2) let note4 = Notification(name: notifName, userInfo: info2) expectEqual(note3, note4) let note5 = Notification(name: notifName, userInfo: info3) expectNotEqual(note1, note5) } @objc func notification(_ notif: Notification) { posted = notif } // MARK: - func test_classForCoder() { // confirm internal bridged impl types are not exposed to archival machinery // we have to be circuitous here, as bridging makes it very difficult to confirm this // // Gated on the availability of NSKeyedArchiver.archivedData(withRootObject:). if #available(macOS 10.11, iOS 9.0, tvOS 9.0, watchOS 2.0, *) { let note = Notification(name: Notification.Name(rawValue: "TestSwiftNotification"), userInfo: [AnyHashable("key"):"value"]) let archivedNote = NSKeyedArchiver.archivedData(withRootObject: note) let noteAsPlist = try! PropertyListSerialization.propertyList(from: archivedNote, options: [], format: nil) let plistAsData = try! PropertyListSerialization.data(fromPropertyList: noteAsPlist, format: .xml, options: 0) let xml = NSString(data: plistAsData, encoding: String.Encoding.utf8.rawValue)! expectEqual(xml.range(of: "_NSUserInfoDictionary").location, NSNotFound) } } func test_AnyHashableContainingNotification() { let values: [Notification] = [ Notification(name: Notification.Name(rawValue: "TestSwiftNotification")), Notification(name: Notification.Name(rawValue: "TestOtherSwiftNotification")), Notification(name: Notification.Name(rawValue: "TestOtherSwiftNotification")), ] let anyHashables = values.map(AnyHashable.init) expectEqual(Notification.self, type(of: anyHashables[0].base)) expectEqual(Notification.self, type(of: anyHashables[1].base)) expectEqual(Notification.self, type(of: anyHashables[2].base)) expectNotEqual(anyHashables[0], anyHashables[1]) expectEqual(anyHashables[1], anyHashables[2]) } func test_AnyHashableCreatedFromNSNotification() { let values: [NSNotification] = [ NSNotification(name: Notification.Name(rawValue: "TestSwiftNotification"), object: nil), NSNotification(name: Notification.Name(rawValue: "TestOtherSwiftNotification"), object: nil), NSNotification(name: Notification.Name(rawValue: "TestOtherSwiftNotification"), object: nil), ] let anyHashables = values.map(AnyHashable.init) expectEqual(Notification.self, type(of: anyHashables[0].base)) expectEqual(Notification.self, type(of: anyHashables[1].base)) expectEqual(Notification.self, type(of: anyHashables[2].base)) expectNotEqual(anyHashables[0], anyHashables[1]) expectEqual(anyHashables[1], anyHashables[2]) } } #if !FOUNDATION_XCTEST var UserInfoTests = TestSuite("UserInfo") UserInfoTests.test("test_userInfoPost") { TestUserInfo().test_userInfoPost() } UserInfoTests.test("test_equality") { TestUserInfo().test_equality() } UserInfoTests.test("test_classForCoder") { TestUserInfo().test_classForCoder() } UserInfoTests.test("test_AnyHashableContainingNotification") { TestUserInfo().test_AnyHashableContainingNotification() } UserInfoTests.test("test_AnyHashableCreatedFromNSNotification") { TestUserInfo().test_AnyHashableCreatedFromNSNotification() } runAllTests() #endif