blob: 834f4b7f5def1c56b2d98ce0a721d14334aaeea9 [file] [log] [blame]
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2019 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
//
//===----------------------------------------------------------------------===//
// Only support 64bit
#if !(os(iOS) && (arch(i386) || arch(arm)))
@_exported import Foundation // Clang module
import Combine
@available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *)
extension NotificationCenter {
/// Returns a publisher that emits events when broadcasting notifications.
///
/// - Parameters:
/// - name: The name of the notification to publish.
/// - object: The object posting the named notfication. If `nil`, the publisher emits elements for any object producing a notification with the given name.
/// - Returns: A publisher that emits events when broadcasting notifications.
public func publisher(
for name: Notification.Name,
object: AnyObject? = nil
) -> NotificationCenter.Publisher {
return NotificationCenter.Publisher(center: self, name: name, object: object)
}
}
@available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *)
extension NotificationCenter {
/// A publisher that emits elements when broadcasting notifications.
public struct Publisher: Combine.Publisher {
public typealias Output = Notification
public typealias Failure = Never
/// The notification center this publisher uses as a source.
public let center: NotificationCenter
/// The name of notifications published by this publisher.
public let name: Notification.Name
/// The object posting the named notfication.
public let object: AnyObject?
/// Creates a publisher that emits events when broadcasting notifications.
///
/// - Parameters:
/// - center: The notification center to publish notifications for.
/// - name: The name of the notification to publish.
/// - object: The object posting the named notfication. If `nil`, the publisher emits elements for any object producing a notification with the given name.
public init(center: NotificationCenter, name: Notification.Name, object: AnyObject? = nil) {
self.center = center
self.name = name
self.object = object
}
public func receive<S: Subscriber>(subscriber: S) where S.Input == Output, S.Failure == Failure {
subscriber.receive(subscription: Notification.Subscription(center, name, object, subscriber))
}
}
}
@available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *)
extension NotificationCenter.Publisher: Equatable {
public static func == (
lhs: NotificationCenter.Publisher,
rhs: NotificationCenter.Publisher
) -> Bool {
return lhs.center === rhs.center
&& lhs.name == rhs.name
&& lhs.object === rhs.object
}
}
@available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *)
extension Notification {
fileprivate final class Subscription<S: Subscriber>: Combine.Subscription, CustomStringConvertible, CustomReflectable, CustomPlaygroundDisplayConvertible
where
S.Input == Notification
{
private let lock = Lock()
// This lock can only be held for the duration of downstream callouts
private let downstreamLock = RecursiveLock()
private var demand: Subscribers.Demand // GuardedBy(lock)
private var center: NotificationCenter? // GuardedBy(lock)
private let name: Notification.Name // Stored only for debug info
private var object: AnyObject? // Stored only for debug info
private var observation: AnyObject? // GuardedBy(lock)
var description: String { return "NotificationCenter Observer" }
var customMirror: Mirror {
lock.lock()
defer { lock.unlock() }
return Mirror(self, children: [
"center": center as Any,
"name": name as Any,
"object": object as Any,
"demand": demand
])
}
var playgroundDescription: Any { return description }
init(_ center: NotificationCenter,
_ name: Notification.Name,
_ object: AnyObject?,
_ next: S)
{
self.demand = .max(0)
self.center = center
self.name = name
self.object = object
self.observation = center.addObserver(
forName: name,
object: object,
queue: nil
) { [weak self] note in
guard let self = self else { return }
self.lock.lock()
guard self.observation != nil else {
self.lock.unlock()
return
}
let demand = self.demand
if demand > 0 {
self.demand -= 1
}
self.lock.unlock()
if demand > 0 {
self.downstreamLock.lock()
let additionalDemand = next.receive(note)
self.downstreamLock.unlock()
if additionalDemand > 0 {
self.lock.lock()
self.demand += additionalDemand
self.lock.unlock()
}
} else {
// Drop it on the floor
}
}
}
deinit {
lock.cleanupLock()
downstreamLock.cleanupLock()
}
func request(_ d: Subscribers.Demand) {
lock.lock()
demand += d
lock.unlock()
}
func cancel() {
lock.lock()
guard let center = self.center,
let observation = self.observation else {
lock.unlock()
return
}
self.center = nil
self.observation = nil
self.object = nil
lock.unlock()
center.removeObserver(observation)
}
}
}
#endif /* !(os(iOS) && (arch(i386) || arch(arm))) */