From 18b6fd31fe48c876598aec45e10b03b006c56910 Mon Sep 17 00:00:00 2001 From: Kyle Date: Wed, 27 Dec 2023 14:37:14 +0800 Subject: [PATCH] =?UTF-8?q?Update=20doc=20and=20align=20with=20AnyPublishe?= =?UTF-8?q?r=20with=20Combine=E2=80=99s=20swiftinterface?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Sources/OpenCombine/AnyPublisher.swift | 99 ++++++------------- Sources/OpenCombine/AnySubscriber.swift | 63 ++++++------ Sources/OpenCombine/Publisher+Subscribe.swift | 1 + .../Publishers/Publishers.Merge.swift | 58 +---------- .../Publishers/Publishers.Throttle.swift | 9 -- .../Publishers/Publishers.Zip.swift | 23 +---- 6 files changed, 68 insertions(+), 185 deletions(-) diff --git a/Sources/OpenCombine/AnyPublisher.swift b/Sources/OpenCombine/AnyPublisher.swift index bfa69ae7..ca2d4366 100644 --- a/Sources/OpenCombine/AnyPublisher.swift +++ b/Sources/OpenCombine/AnyPublisher.swift @@ -1,29 +1,18 @@ // // AnyPublisher.swift -// +// OpenCombine // // Created by Sergej Jaskiewicz on 10.06.2019. -// +// Audited for Combine 2023 extension Publisher { - /// Wraps this publisher with a type eraser. /// - /// Use `eraseToAnyPublisher()` to expose an instance of `AnyPublisher`` to - /// the downstream subscriber, rather than this publisher’s actual type. - /// This form of _type erasure_ preserves abstraction across API boundaries, such as - /// different modules. - /// When you expose your publishers as the `AnyPublisher` type, you can change - /// the underlying implementation over time without affecting existing clients. + /// Use ``Publisher/eraseToAnyPublisher()`` to expose an instance of ``AnyPublisher`` to the downstream subscriber, rather than this publisher’s actual type. + /// This form of _type erasure_ preserves abstraction across API boundaries, such as different modules. + /// When you expose your publishers as the ``AnyPublisher`` type, you can change the underlying implementation over time without affecting existing clients. /// - /// The following example shows two types that each have a `publisher` property. - /// `TypeWithSubject` exposes this property as its actual type, `PassthroughSubject`, - /// while `TypeWithErasedSubject` uses `eraseToAnyPublisher()` to expose it as - /// an `AnyPublisher`. As seen in the output, a caller from another module can access - /// `TypeWithSubject.publisher` as its native type. This means you can’t change your - /// publisher to a different type without breaking the caller. By comparison, - /// `TypeWithErasedSubject.publisher` appears to callers as an `AnyPublisher`, so you - /// can change the underlying publisher type at will. + /// The following example shows two types that each have a `publisher` property. `TypeWithSubject` exposes this property as its actual type, ``PassthroughSubject``, while `TypeWithErasedSubject` uses ``Publisher/eraseToAnyPublisher()`` to expose it as an ``AnyPublisher``. As seen in the output, a caller from another module can access `TypeWithSubject.publisher` as its native type. This means you can’t change your publisher to a different type without breaking the caller. By comparison, `TypeWithErasedSubject.publisher` appears to callers as an ``AnyPublisher``, so you can change the underlying publisher type at will. /// /// public class TypeWithSubject { /// public let publisher: some Publisher = PassthroughSubject() @@ -48,65 +37,44 @@ extension Publisher { /// - Returns: An ``AnyPublisher`` wrapping this publisher. @inlinable public func eraseToAnyPublisher() -> AnyPublisher { - return .init(self) + AnyPublisher(self) } } -/// A type-erasing publisher. +/// A publisher that performs type erasure by wrapping another publisher. /// -/// Use `AnyPublisher` to wrap a publisher whose type has details you don’t want to expose -/// across API boundaries, such as different modules. Wrapping a `Subject` with -/// `AnyPublisher` also prevents callers from accessing its `send(_:)` method. When you -/// use type erasure this way, you can change the underlying publisher implementation over -/// time without affecting existing clients. +/// ``AnyPublisher`` is a concrete implementation of ``Publisher`` that has no significant properties of its own, and passes through elements and completion values from its upstream publisher. /// -/// You can use OpenCombine’s `eraseToAnyPublisher()` operator to wrap a publisher with -/// `AnyPublisher`. +/// Use ``AnyPublisher`` to wrap a publisher whose type has details you don’t want to expose across API boundaries, such as different modules. Wrapping a ``Subject`` with ``AnyPublisher`` also prevents callers from accessing its ``Subject/send(_:)`` method. When you use type erasure this way, you can change the underlying publisher implementation over time without affecting existing clients. +/// +/// You can use OpenCombine’s ``Publisher/eraseToAnyPublisher()`` operator to wrap a publisher with ``AnyPublisher``. @frozen -public struct AnyPublisher - : CustomStringConvertible, - CustomPlaygroundDisplayConvertible +public struct AnyPublisher: CustomStringConvertible, CustomPlaygroundDisplayConvertible { @usableFromInline - internal let box: PublisherBoxBase + let box: PublisherBoxBase + + public var description: String { "AnyPublisher" } + public var playgroundDescription: Any { description } + /// Creates a type-erasing publisher to wrap the provided publisher. /// /// - Parameter publisher: A publisher to wrap with a type-eraser. @inlinable - public init(_ publisher: PublisherType) - where Output == PublisherType.Output, Failure == PublisherType.Failure - { + public init

(_ publisher: P) where Output == P.Output, Failure == P.Failure, P: Publisher { // If this has already been boxed, avoid boxing again if let erased = publisher as? AnyPublisher { box = erased.box } else { - box = PublisherBox(base: publisher) + box = PublisherBox(publisher) } } - - public var description: String { - return "AnyPublisher" - } - - public var playgroundDescription: Any { - return description - } } extension AnyPublisher: Publisher { - - /// This function is called to attach the specified `Subscriber` to this `Publisher` - /// by `subscribe(_:)` - /// - /// - SeeAlso: `subscribe(_:)` - /// - Parameters: - /// - subscriber: The subscriber to attach to this `Publisher`. - /// once attached it can begin to receive values. @inlinable - public func receive(subscriber: Downstream) - where Output == Downstream.Input, Failure == Downstream.Failure - { + public func receive(subscriber: S) where Output == S.Input, Failure == S.Failure, S: Subscriber { box.receive(subscriber: subscriber) } } @@ -115,36 +83,33 @@ extension AnyPublisher: Publisher { /// publisher. @usableFromInline @_fixed_layout -internal class PublisherBoxBase: Publisher { - +class PublisherBoxBase: Publisher { @inlinable - internal init() {} + init() {} + + @inlinable deinit {} @usableFromInline - internal func receive(subscriber: Downstream) - where Failure == Downstream.Failure, Output == Downstream.Input - { + func receive(subscriber _: S) where Output == S.Input, Failure == S.Failure, S: Subscriber { abstractMethod() } } @usableFromInline @_fixed_layout -internal final class PublisherBox - : PublisherBoxBase -{ +final class PublisherBox: PublisherBoxBase { @usableFromInline - internal let base: PublisherType + let base: Base @inlinable - internal init(base: PublisherType) { + init(_ base: Base) { self.base = base - super.init() } + + @inlinable deinit {} @inlinable - override internal func receive(subscriber: Downstream) - where Failure == Downstream.Failure, Output == Downstream.Input + override final func receive(subscriber: S) where Failure == S.Failure, Output == S.Input { base.receive(subscriber: subscriber) } diff --git a/Sources/OpenCombine/AnySubscriber.swift b/Sources/OpenCombine/AnySubscriber.swift index ea86b38e..ebfc7d06 100644 --- a/Sources/OpenCombine/AnySubscriber.swift +++ b/Sources/OpenCombine/AnySubscriber.swift @@ -43,37 +43,40 @@ public struct AnySubscriber: Subscriber, /// - Parameter s: The subscriber to type-erase. @inline(__always) @inlinable - public init(_ subscriber: Subscriber) - where Input == Subscriber.Input, Failure == Subscriber.Failure - { - if let erased = subscriber as? AnySubscriber { + public init(_ s: S) where Input == S.Input, Failure == S.Failure, S: Subscriber { + if let erased = s as? AnySubscriber { self = erased - return - } - - combineIdentifier = subscriber.combineIdentifier - - box = AnySubscriberBox(subscriber) - - if let description = subscriber as? CustomStringConvertible { - descriptionThunk = { description.description } - } else { - let fixedDescription = String(describing: type(of: subscriber)) - descriptionThunk = { fixedDescription } - } - - customMirrorThunk = { - (subscriber as? CustomReflectable)?.customMirror - ?? Mirror(subscriber, children: EmptyCollection()) - } - - if let playgroundDescription = subscriber as? CustomPlaygroundDisplayConvertible { - playgroundDescriptionThunk = { playgroundDescription.playgroundDescription } - } else if let description = subscriber as? CustomStringConvertible { - playgroundDescriptionThunk = { description.description } } else { - let fixedDescription = String(describing: type(of: subscriber)) - playgroundDescriptionThunk = { fixedDescription } + combineIdentifier = s.combineIdentifier + + box = AnySubscriberBox(s) + + // The following use normal memory management semantics + if let desc = s as? CustomStringConvertible { + descriptionThunk = { + desc.description + } + } else { + let fixedDescription = "\(type(of: s))" + descriptionThunk = { fixedDescription } + } + + customMirrorThunk = { + if let mir = s as? CustomReflectable { + return mir.customMirror + } else { + return Mirror(s, children: [:]) + } + } + + if let play = s as? CustomPlaygroundDisplayConvertible { + playgroundDescriptionThunk = { play.playgroundDescription } + } else if let desc = s as? CustomStringConvertible { + playgroundDescriptionThunk = { desc.description } + } else { + let fixedDescription = "\(type(of: s))" + playgroundDescriptionThunk = { fixedDescription } + } } } @@ -119,7 +122,7 @@ public struct AnySubscriber: Subscriber, @inline(__always) @inlinable public func receive(_ value: Input) -> Subscribers.Demand { - return box.receive(value) + box.receive(value) } @inline(__always) diff --git a/Sources/OpenCombine/Publisher+Subscribe.swift b/Sources/OpenCombine/Publisher+Subscribe.swift index 6d4252a2..032115c3 100644 --- a/Sources/OpenCombine/Publisher+Subscribe.swift +++ b/Sources/OpenCombine/Publisher+Subscribe.swift @@ -17,6 +17,7 @@ extension Publisher { /// - Parameters: /// - subscriber: The subscriber to attach to this `Publisher`. After attaching, /// the subscriber can start to receive values. + @inline(never) public func subscribe(_ subscriber: Subscriber) where Failure == Subscriber.Failure, Output == Subscriber.Input { diff --git a/Sources/OpenCombine/Publishers/Publishers.Merge.swift b/Sources/OpenCombine/Publishers/Publishers.Merge.swift index 45b1648e..e9544d27 100644 --- a/Sources/OpenCombine/Publishers/Publishers.Merge.swift +++ b/Sources/OpenCombine/Publishers/Publishers.Merge.swift @@ -1,6 +1,6 @@ // // Publishers.Merge.swift -// +// OpenCombine // // Created by Kyle on 2023/11/21. // Audited for Combine 2023 @@ -355,13 +355,6 @@ extension Publishers { self.b = b } - /// Attaches the specified subscriber to this publisher. - /// - /// Implementations of ``Publisher`` must implement this method. - /// - /// The provided implementation of ``Publisher/subscribe(_:)-199o9``calls this method. - /// - /// - Parameter subscriber: The subscriber to attach to this ``Publisher``, after which it can receive values. public func receive(subscriber: S) where S: Subscriber, B.Failure == S.Failure, B.Output == S.Input { typealias Inner = _Merged let merger = Inner(downstream: subscriber, count: 2) @@ -427,13 +420,6 @@ extension Publishers { self.c = c } - /// Attaches the specified subscriber to this publisher. - /// - /// Implementations of ``Publisher`` must implement this method. - /// - /// The provided implementation of ``Publisher/subscribe(_:)-199o9``calls this method. - /// - /// - Parameter subscriber: The subscriber to attach to this ``Publisher``, after which it can receive values. public func receive(subscriber: S) where S: Subscriber, C.Failure == S.Failure, C.Output == S.Input { typealias Inner = _Merged let merger = Inner(downstream: subscriber, count: 3) @@ -501,13 +487,6 @@ extension Publishers { self.d = d } - /// Attaches the specified subscriber to this publisher. - /// - /// Implementations of ``Publisher`` must implement this method. - /// - /// The provided implementation of ``Publisher/subscribe(_:)-199o9``calls this method. - /// - /// - Parameter subscriber: The subscriber to attach to this ``Publisher``, after which it can receive values. public func receive(subscriber: S) where S: Subscriber, D.Failure == S.Failure, D.Output == S.Input { typealias Inner = _Merged let merger = Inner(downstream: subscriber, count: 4) @@ -577,13 +556,6 @@ extension Publishers { self.e = e } - /// Attaches the specified subscriber to this publisher. - /// - /// Implementations of ``Publisher`` must implement this method. - /// - /// The provided implementation of ``Publisher/subscribe(_:)-199o9``calls this method. - /// - /// - Parameter subscriber: The subscriber to attach to this ``Publisher``, after which it can receive values. public func receive(subscriber: S) where S: Subscriber, E.Failure == S.Failure, E.Output == S.Input { typealias Inner = _Merged let merger = Inner(downstream: subscriber, count: 5) @@ -655,13 +627,6 @@ extension Publishers { self.f = f } - /// Attaches the specified subscriber to this publisher. - /// - /// Implementations of ``Publisher`` must implement this method. - /// - /// The provided implementation of ``Publisher/subscribe(_:)-199o9``calls this method. - /// - /// - Parameter subscriber: The subscriber to attach to this ``Publisher``, after which it can receive values. public func receive(subscriber: S) where S: Subscriber, F.Failure == S.Failure, F.Output == S.Input { typealias Inner = _Merged let merger = Inner(downstream: subscriber, count: 6) @@ -735,13 +700,6 @@ extension Publishers { self.g = g } - /// Attaches the specified subscriber to this publisher. - /// - /// Implementations of ``Publisher`` must implement this method. - /// - /// The provided implementation of ``Publisher/subscribe(_:)-199o9``calls this method. - /// - /// - Parameter subscriber: The subscriber to attach to this ``Publisher``, after which it can receive values. public func receive(subscriber: S) where S: Subscriber, G.Failure == S.Failure, G.Output == S.Input { typealias Inner = _Merged let merger = Inner(downstream: subscriber, count: 7) @@ -817,13 +775,6 @@ extension Publishers { self.h = h } - /// Attaches the specified subscriber to this publisher. - /// - /// Implementations of ``Publisher`` must implement this method. - /// - /// The provided implementation of ``Publisher/subscribe(_:)-199o9``calls this method. - /// - /// - Parameter subscriber: The subscriber to attach to this ``Publisher``, after which it can receive values. public func receive(subscriber: S) where S: Subscriber, H.Failure == S.Failure, H.Output == S.Input { typealias Inner = _Merged let merger = Inner(downstream: subscriber, count: 8) @@ -866,13 +817,6 @@ extension Publishers { publishers = Array(upstream) } - /// Attaches the specified subscriber to this publisher. - /// - /// Implementations of ``Publisher`` must implement this method. - /// - /// The provided implementation of ``Publisher/subscribe(_:)-199o9``calls this method. - /// - /// - Parameter subscriber: The subscriber to attach to this ``Publisher``, after which it can receive values. public func receive(subscriber: S) where S: Subscriber, Upstream.Failure == S.Failure, Upstream.Output == S.Input { typealias Inner = _Merged let merger = Inner(downstream: subscriber, count: publishers.count) diff --git a/Sources/OpenCombine/Publishers/Publishers.Throttle.swift b/Sources/OpenCombine/Publishers/Publishers.Throttle.swift index f3d9ec95..e0f8ec24 100644 --- a/Sources/OpenCombine/Publishers/Publishers.Throttle.swift +++ b/Sources/OpenCombine/Publishers/Publishers.Throttle.swift @@ -103,15 +103,6 @@ extension Publishers { self.latest = latest } - /// Attaches the specified subscriber to this publisher. - /// - /// Implementations of ``Publisher`` must implement this method. - /// - /// The provided implementation of ``Publisher/subscribe(_:)-199o9``calls - /// this method. - /// - /// - Parameter subscriber: The subscriber to attach to this ``Publisher``, - /// after which it can receive values. // swiftlint:disable generic_type_name public func receive(subscriber: S) where S: Subscriber, Upstream.Failure == S.Failure, Upstream.Output == S.Input diff --git a/Sources/OpenCombine/Publishers/Publishers.Zip.swift b/Sources/OpenCombine/Publishers/Publishers.Zip.swift index 4b003fd3..9b5c0060 100644 --- a/Sources/OpenCombine/Publishers/Publishers.Zip.swift +++ b/Sources/OpenCombine/Publishers/Publishers.Zip.swift @@ -1,6 +1,6 @@ // // Publishers.Zip.swift -// +// OpenCombine // // Created by Kyle on 2023/7/25. // Audited for Combine 2023 @@ -266,13 +266,6 @@ extension Publishers { self.b = b } - /// Attaches the specified subscriber to this publisher. - /// - /// Implementations of ``Publisher`` must implement this method. - /// - /// The provided implementation of ``Publisher/subscribe(_:)-199o9``calls this method. - /// - /// - Parameter subscriber: The subscriber to attach to this ``Publisher``, after which it can receive values. public func receive(subscriber: S) where S: Subscriber, B.Failure == S.Failure, S.Input == (A.Output, B.Output) { typealias Inner = Zip2Inner let zip = Inner(downstream: subscriber, upstreamCount: 2) @@ -317,13 +310,6 @@ extension Publishers { self.c = c } - /// Attaches the specified subscriber to this publisher. - /// - /// Implementations of ``Publisher`` must implement this method. - /// - /// The provided implementation of ``Publisher/subscribe(_:)-199o9``calls this method. - /// - /// - Parameter subscriber: The subscriber to attach to this ``Publisher``, after which it can receive values. public func receive(subscriber: S) where S: Subscriber, C.Failure == S.Failure, S.Input == (A.Output, B.Output, C.Output) { typealias Inner = Zip3Inner let zip = Inner(downstream: subscriber, upstreamCount: 3) @@ -374,13 +360,6 @@ extension Publishers { self.d = d } - /// Attaches the specified subscriber to this publisher. - /// - /// Implementations of ``Publisher`` must implement this method. - /// - /// The provided implementation of ``Publisher/subscribe(_:)-199o9``calls this method. - /// - /// - Parameter subscriber: The subscriber to attach to this ``Publisher``, after which it can receive values. public func receive(subscriber: S) where S: Subscriber, D.Failure == S.Failure, S.Input == (A.Output, B.Output, C.Output, D.Output) { typealias Inner = Zip4Inner let zip = Inner(downstream: subscriber, upstreamCount: 4)