Add documentation comments on most public API

This commit is contained in:
Sami Samhuri 2025-04-26 11:38:44 -07:00
parent b8eb878097
commit bc05e17c92
No known key found for this signature in database
5 changed files with 63 additions and 22 deletions

View file

@ -1,7 +1,9 @@
/// Type-erasing wrapper for ``AsyncCancellable`` that ties its instance lifetime to cancellation. In other words, when you release
/// an instance of ``AnyAsyncCancellable`` and it's deallocated then it automatically cancels its given ``AsyncCancellable``.
public class AnyAsyncCancellable: AsyncCancellable { public class AnyAsyncCancellable: AsyncCancellable {
let canceller: () -> Void let canceller: () -> Void
init<AC: AsyncCancellable>(cancellable: AC) { public init<AC: AsyncCancellable>(cancellable: AC) {
canceller = { cancellable.cancel() } canceller = { cancellable.cancel() }
} }
@ -14,14 +16,4 @@ public class AnyAsyncCancellable: AsyncCancellable {
public func cancel() { public func cancel() {
canceller() canceller()
} }
// MARK: Hashable conformance
public static func == (lhs: AnyAsyncCancellable, rhs: AnyAsyncCancellable) -> Bool {
ObjectIdentifier(lhs) == ObjectIdentifier(rhs)
}
public func hash(into hasher: inout Hasher) {
hasher.combine(ObjectIdentifier(self))
}
} }

View file

@ -1,11 +1,29 @@
public protocol AsyncCancellable: Hashable { /// Represents an async operation that can be cancelled.
public protocol AsyncCancellable: AnyObject, Hashable {
/// Cancels the operation.
func cancel() func cancel()
/// Stores this cancellable in the given set, using the type-erasing wrapper ``AnyAsyncCancellable``. This method has a
/// default implementation and you typically shouldn't need to override it.
func store(in set: inout Set<AnyAsyncCancellable>) func store(in set: inout Set<AnyAsyncCancellable>)
} }
// MARK: Default implementations
public extension AsyncCancellable { public extension AsyncCancellable {
func store(in set: inout Set<AnyAsyncCancellable>) { func store(in set: inout Set<AnyAsyncCancellable>) {
set.insert(AnyAsyncCancellable(cancellable: self)) set.insert(AnyAsyncCancellable(cancellable: self))
} }
} }
// MARK: Hashable conformance
public extension AsyncCancellable {
static func == (lhs: Self, rhs: Self) -> Bool {
ObjectIdentifier(lhs) == ObjectIdentifier(rhs)
}
func hash(into hasher: inout Hasher) {
hasher.combine(ObjectIdentifier(self))
}
}

View file

@ -1,6 +1,24 @@
/// A monitor that observes an asynchronous sequence and invokes the given block for each received element.
///
/// The element must be `Sendable` so to use it to monitor notifications from `NotificationCenter` you'll need to map them to
/// something sendable before calling `monitor` on the sequence. e.g.
///
/// ```
/// NotificationCenter.default
/// .notifications(named: .NSCalendarDayChanged).map(\.name)
/// .monitor { _ in whatever() }
/// .store(in: &cancellables)
/// ```
public final class AsyncMonitor: Hashable, AsyncCancellable { public final class AsyncMonitor: Hashable, AsyncCancellable {
let task: Task<Void, Never> let task: Task<Void, Never>
/// Creates an ``AsyncMonitor`` that observes the provided asynchronous sequence.
///
/// - Parameters:
/// - isolation: An optional actor isolation context to inherit.
/// Defaults to `#isolation`, preserving the caller's actor isolation.
/// - sequence: The asynchronous sequence of elements to observe.
/// - block: A closure to execute for each element yielded by the sequence.
public init<Element: Sendable>( public init<Element: Sendable>(
isolation: isolated (any Actor)? = #isolation, isolation: isolated (any Actor)? = #isolation,
sequence: any AsyncSequence<Element, Never>, sequence: any AsyncSequence<Element, Never>,
@ -21,17 +39,8 @@ public final class AsyncMonitor: Hashable, AsyncCancellable {
// MARK: AsyncCancellable conformance // MARK: AsyncCancellable conformance
/// Cancels the underlying task monitoring the asynchronous sequence.
public func cancel() { public func cancel() {
task.cancel() task.cancel()
} }
// MARK: Hashable conformance
public static func == (lhs: AsyncMonitor, rhs: AsyncMonitor) -> Bool {
lhs.task == rhs.task
}
public func hash(into hasher: inout Hasher) {
hasher.combine(task)
}
} }

View file

@ -1,4 +1,11 @@
public extension AsyncSequence where Element: Sendable, Failure == Never { public extension AsyncSequence where Element: Sendable, Failure == Never {
/// Observes the elements yielded by this sequence and executes the given closure with each element.
///
/// This method preserves the actor isolation of the caller by default when `isolation` is not specified.
///
/// - Parameters:
/// - isolation: An optional actor isolation context to inherit. Defaults to `#isolation`, preserving the caller's actor isolation.
/// - block: A closure that's executed with each yielded element.
func monitor( func monitor(
isolation: isolated (any Actor)? = #isolation, isolation: isolated (any Actor)? = #isolation,
_ block: @escaping (Element) async -> Void _ block: @escaping (Element) async -> Void
@ -6,6 +13,15 @@ public extension AsyncSequence where Element: Sendable, Failure == Never {
AsyncMonitor(isolation: isolation, sequence: self, performing: block) AsyncMonitor(isolation: isolation, sequence: self, performing: block)
} }
/// Observes the elements yielded by this sequence and executes the given closure with each element the weakly-captured
/// context object.
///
/// This method preserves the actor isolation of the caller by default when `isolation` is not specified.
///
/// - Parameters:
/// - isolation: An optional actor isolation context to inherit. Defaults to `#isolation`, preserving the caller's actor isolation.
/// - context: The object to capture weakly for use within the closure.
/// - block: A closure that's executed with each yielded element, and the `context`.
func monitor<Context: AnyObject>( func monitor<Context: AnyObject>(
isolation: isolated (any Actor)? = #isolation, isolation: isolated (any Actor)? = #isolation,
context: Context, context: Context,

View file

@ -3,6 +3,12 @@ public import Foundation
extension KeyPath: @unchecked @retroactive Sendable where Value: Sendable {} extension KeyPath: @unchecked @retroactive Sendable where Value: Sendable {}
public extension NSObjectProtocol where Self: NSObject { public extension NSObjectProtocol where Self: NSObject {
/// Observes changes to the specified key path on the object and asynchronously yields each value. Values must be `Sendable`.
///
/// - Parameters:
/// - keyPath: The key path to observe on this object. The value must be `Sendable`.
/// - options: KVO options to use for observation. Defaults to an empty set.
/// - changeHandler: A closure that's executed with each new value.
func values<Value: Sendable>( func values<Value: Sendable>(
for keyPath: KeyPath<Self, Value>, for keyPath: KeyPath<Self, Value>,
options: NSKeyValueObservingOptions = [], options: NSKeyValueObservingOptions = [],