blob: 15b2e78f139861df0173f366e963ead2670ce683 [file] [log] [blame]
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2016 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See http://swift.org/LICENSE.txt for license information
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
open class NSNotification: NSObject, NSCopying, NSCoding {
public struct Name : RawRepresentable, Equatable, Hashable {
public private(set) var rawValue: String
public init(_ rawValue: String) {
self.rawValue = rawValue
}
public init(rawValue: String) {
self.rawValue = rawValue
}
}
private(set) open var name: Name
private(set) open var object: Any?
private(set) open var userInfo: [AnyHashable : Any]?
public convenience override init() {
/* do not invoke; not a valid initializer for this class */
fatalError()
}
public init(name: Name, object: Any?, userInfo: [AnyHashable : Any]? = nil) {
self.name = name
self.object = object
self.userInfo = userInfo
}
public convenience required init?(coder aDecoder: NSCoder) {
guard aDecoder.allowsKeyedCoding else {
preconditionFailure("Unkeyed coding is unsupported.")
}
guard let name = aDecoder.decodeObject(of: NSString.self, forKey:"NS.name") else {
return nil
}
let object = aDecoder.decodeObject(forKey: "NS.object")
// let userInfo = aDecoder.decodeObject(of: NSDictionary.self, forKey: "NS.userinfo")
self.init(name: Name(rawValue: String._unconditionallyBridgeFromObjectiveC(name)), object: object as! NSObject, userInfo: nil)
}
open func encode(with aCoder: NSCoder) {
guard aCoder.allowsKeyedCoding else {
preconditionFailure("Unkeyed coding is unsupported.")
}
aCoder.encode(self.name.rawValue._bridgeToObjectiveC(), forKey:"NS.name")
aCoder.encode(self.object, forKey:"NS.object")
aCoder.encode(self.userInfo?._bridgeToObjectiveC(), forKey:"NS.userinfo")
}
open override func copy() -> Any {
return copy(with: nil)
}
open func copy(with zone: NSZone? = nil) -> Any {
return self
}
open override var description: String {
var str = "\(type(of: self)) \(Unmanaged.passUnretained(self).toOpaque()) {"
str += "name = \(self.name.rawValue)"
if let object = self.object {
str += "; object = \(object)"
}
if let userInfo = self.userInfo {
str += "; userInfo = \(userInfo)"
}
str += "}"
return str
}
}
private class NSNotificationReceiver : NSObject {
fileprivate var name: Notification.Name?
fileprivate var block: ((Notification) -> Void)?
fileprivate var sender: AnyObject?
fileprivate var queue: OperationQueue?
}
private let _defaultCenter: NotificationCenter = NotificationCenter()
open class NotificationCenter: NSObject {
private lazy var _nilIdentifier: ObjectIdentifier = ObjectIdentifier(_observersLock)
private lazy var _nilHashable: AnyHashable = AnyHashable(_nilIdentifier)
private var _observers: [AnyHashable /* Notification.Name */ : [ObjectIdentifier /* object */ : [ObjectIdentifier /* notification receiver */ : NSNotificationReceiver]]]
private let _observersLock = NSLock()
public required override init() {
_observers = [AnyHashable: [ObjectIdentifier: [ObjectIdentifier: NSNotificationReceiver]]]()
}
open class var `default`: NotificationCenter {
return _defaultCenter
}
open func post(_ notification: Notification) {
let notificationNameIdentifier: AnyHashable = AnyHashable(notification.name)
let senderIdentifier: ObjectIdentifier? = notification.object.map({ ObjectIdentifier(__SwiftValue.store($0)) })
let sendTo: [Dictionary<ObjectIdentifier, NSNotificationReceiver>.Values] = _observersLock.synchronized({
var retVal = [Dictionary<ObjectIdentifier, NSNotificationReceiver>.Values]()
(_observers[_nilHashable]?[_nilIdentifier]?.values).map({ retVal.append($0) })
senderIdentifier.flatMap({ _observers[_nilHashable]?[$0]?.values }).map({ retVal.append($0) })
(_observers[notificationNameIdentifier]?[_nilIdentifier]?.values).map({ retVal.append($0) })
senderIdentifier.flatMap({ _observers[notificationNameIdentifier]?[$0]?.values}).map({ retVal.append($0) })
return retVal
})
sendTo.forEach { observers in
observers.forEach { observer in
guard let block = observer.block else {
return
}
if let queue = observer.queue, queue != OperationQueue.current {
queue.addOperation { block(notification) }
queue.waitUntilAllOperationsAreFinished()
} else {
block(notification)
}
}
}
}
open func post(name aName: NSNotification.Name, object anObject: Any?, userInfo aUserInfo: [AnyHashable : Any]? = nil) {
let notification = Notification(name: aName, object: anObject, userInfo: aUserInfo)
post(notification)
}
open func removeObserver(_ observer: Any) {
removeObserver(observer, name: nil, object: nil)
}
open func removeObserver(_ observer: Any, name aName: NSNotification.Name?, object: Any?) {
guard let observer = observer as? NSNotificationReceiver,
// These 2 parameters would only be useful for removing notifications added by `addObserver:selector:name:object:`
aName == nil || observer.name == aName,
object == nil || observer.sender === __SwiftValue.store(object)
else {
return
}
let notificationNameIdentifier: AnyHashable = observer.name.map { AnyHashable($0) } ?? _nilHashable
let senderIdentifier: ObjectIdentifier = observer.sender.map { ObjectIdentifier($0) } ?? _nilIdentifier
let receiverIdentifier: ObjectIdentifier = ObjectIdentifier(observer)
_observersLock.synchronized({
_observers[notificationNameIdentifier]?[senderIdentifier]?.removeValue(forKey: receiverIdentifier)
if _observers[notificationNameIdentifier]?[senderIdentifier]?.count == 0 {
_observers[notificationNameIdentifier]?.removeValue(forKey: senderIdentifier)
}
})
}
@available(*, unavailable, renamed: "addObserver(forName:object:queue:using:)")
open func addObserver(forName name: NSNotification.Name?, object obj: Any?, queue: OperationQueue?, usingBlock block: @escaping (Notification) -> Void) -> NSObjectProtocol {
return addObserver(forName: name, object: obj, queue: queue, using: block)
}
open func addObserver(forName name: NSNotification.Name?, object obj: Any?, queue: OperationQueue?, using block: @escaping (Notification) -> Void) -> NSObjectProtocol {
let newObserver = NSNotificationReceiver()
newObserver.name = name
newObserver.block = block
newObserver.sender = __SwiftValue.store(obj)
newObserver.queue = queue
let notificationNameIdentifier: AnyHashable = name.map({ AnyHashable($0) }) ?? _nilHashable
let senderIdentifier: ObjectIdentifier = newObserver.sender.map({ ObjectIdentifier($0) }) ?? _nilIdentifier
let receiverIdentifier: ObjectIdentifier = ObjectIdentifier(newObserver)
_observersLock.synchronized({
_observers[notificationNameIdentifier, default: [:]][senderIdentifier, default: [:]][receiverIdentifier] = newObserver
})
return newObserver
}
}