Skip to content

Commit

Permalink
Update doc and align with AnyPublisher with Combine’s swiftinterface
Browse files Browse the repository at this point in the history
  • Loading branch information
Kyle-Ye committed Dec 27, 2023
1 parent 6ba91aa commit 18b6fd3
Show file tree
Hide file tree
Showing 6 changed files with 68 additions and 185 deletions.
99 changes: 32 additions & 67 deletions Sources/OpenCombine/AnyPublisher.swift
Original file line number Diff line number Diff line change
@@ -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<Int,Never>()
Expand All @@ -48,65 +37,44 @@ extension Publisher {
/// - Returns: An ``AnyPublisher`` wrapping this publisher.
@inlinable
public func eraseToAnyPublisher() -> AnyPublisher<Output, Failure> {
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<Output, Failure: Error>
: CustomStringConvertible,
CustomPlaygroundDisplayConvertible
public struct AnyPublisher<Output, Failure: Error>: CustomStringConvertible, CustomPlaygroundDisplayConvertible
{
@usableFromInline
internal let box: PublisherBoxBase<Output, Failure>
let box: PublisherBoxBase<Output, Failure>

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<PublisherType: Publisher>(_ publisher: PublisherType)
where Output == PublisherType.Output, Failure == PublisherType.Failure
{
public init<P>(_ 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<Output, Failure> {
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<Downstream: Subscriber>(subscriber: Downstream)
where Output == Downstream.Input, Failure == Downstream.Failure
{
public func receive<S>(subscriber: S) where Output == S.Input, Failure == S.Failure, S: Subscriber {
box.receive(subscriber: subscriber)
}
}
Expand All @@ -115,36 +83,33 @@ extension AnyPublisher: Publisher {
/// publisher.
@usableFromInline
@_fixed_layout
internal class PublisherBoxBase<Output, Failure: Error>: Publisher {

class PublisherBoxBase<Output, Failure: Error>: Publisher {
@inlinable
internal init() {}
init() {}

@inlinable deinit {}

@usableFromInline
internal func receive<Downstream: Subscriber>(subscriber: Downstream)
where Failure == Downstream.Failure, Output == Downstream.Input
{
func receive<S>(subscriber _: S) where Output == S.Input, Failure == S.Failure, S: Subscriber {
abstractMethod()
}
}

@usableFromInline
@_fixed_layout
internal final class PublisherBox<PublisherType: Publisher>
: PublisherBoxBase<PublisherType.Output, PublisherType.Failure>
{
final class PublisherBox<Base: Publisher>: PublisherBoxBase<Base.Output, Base.Failure> {
@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<Downstream: Subscriber>(subscriber: Downstream)
where Failure == Downstream.Failure, Output == Downstream.Input
override final func receive<S: Subscriber>(subscriber: S) where Failure == S.Failure, Output == S.Input
{
base.receive(subscriber: subscriber)
}
Expand Down
63 changes: 33 additions & 30 deletions Sources/OpenCombine/AnySubscriber.swift
Original file line number Diff line number Diff line change
Expand Up @@ -43,37 +43,40 @@ public struct AnySubscriber<Input, Failure: Error>: Subscriber,
/// - Parameter s: The subscriber to type-erase.
@inline(__always)
@inlinable
public init<Subscriber: OpenCombine.Subscriber>(_ subscriber: Subscriber)
where Input == Subscriber.Input, Failure == Subscriber.Failure
{
if let erased = subscriber as? AnySubscriber<Input, Failure> {
public init<S>(_ s: S) where Input == S.Input, Failure == S.Failure, S: Subscriber {
if let erased = s as? AnySubscriber<Input, Failure> {
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 }
}
}
}

Expand Down Expand Up @@ -119,7 +122,7 @@ public struct AnySubscriber<Input, Failure: Error>: Subscriber,
@inline(__always)
@inlinable
public func receive(_ value: Input) -> Subscribers.Demand {
return box.receive(value)
box.receive(value)
}

@inline(__always)
Expand Down
1 change: 1 addition & 0 deletions Sources/OpenCombine/Publisher+Subscribe.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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: OpenCombine.Subscriber>(_ subscriber: Subscriber)
where Failure == Subscriber.Failure, Output == Subscriber.Input
{
Expand Down
58 changes: 1 addition & 57 deletions Sources/OpenCombine/Publishers/Publishers.Merge.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//
// Publishers.Merge.swift
//
// OpenCombine
//
// Created by Kyle on 2023/11/21.
// Audited for Combine 2023
Expand Down Expand Up @@ -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<S>(subscriber: S) where S: Subscriber, B.Failure == S.Failure, B.Output == S.Input {
typealias Inner = _Merged<A.Output, Failure, S>
let merger = Inner(downstream: subscriber, count: 2)
Expand Down Expand Up @@ -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<S>(subscriber: S) where S: Subscriber, C.Failure == S.Failure, C.Output == S.Input {
typealias Inner = _Merged<A.Output, Failure, S>
let merger = Inner(downstream: subscriber, count: 3)
Expand Down Expand Up @@ -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<S>(subscriber: S) where S: Subscriber, D.Failure == S.Failure, D.Output == S.Input {
typealias Inner = _Merged<A.Output, Failure, S>
let merger = Inner(downstream: subscriber, count: 4)
Expand Down Expand Up @@ -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<S>(subscriber: S) where S: Subscriber, E.Failure == S.Failure, E.Output == S.Input {
typealias Inner = _Merged<A.Output, Failure, S>
let merger = Inner(downstream: subscriber, count: 5)
Expand Down Expand Up @@ -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<S>(subscriber: S) where S: Subscriber, F.Failure == S.Failure, F.Output == S.Input {
typealias Inner = _Merged<A.Output, Failure, S>
let merger = Inner(downstream: subscriber, count: 6)
Expand Down Expand Up @@ -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<S>(subscriber: S) where S: Subscriber, G.Failure == S.Failure, G.Output == S.Input {
typealias Inner = _Merged<A.Output, Failure, S>
let merger = Inner(downstream: subscriber, count: 7)
Expand Down Expand Up @@ -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<S>(subscriber: S) where S: Subscriber, H.Failure == S.Failure, H.Output == S.Input {
typealias Inner = _Merged<A.Output, Failure, S>
let merger = Inner(downstream: subscriber, count: 8)
Expand Down Expand Up @@ -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<S>(subscriber: S) where S: Subscriber, Upstream.Failure == S.Failure, Upstream.Output == S.Input {
typealias Inner = _Merged<Upstream.Output, Failure, S>
let merger = Inner(downstream: subscriber, count: publishers.count)
Expand Down
9 changes: 0 additions & 9 deletions Sources/OpenCombine/Publishers/Publishers.Throttle.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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<S>(subscriber: S)
where S: Subscriber, Upstream.Failure == S.Failure, Upstream.Output == S.Input
Expand Down
Loading

0 comments on commit 18b6fd3

Please sign in to comment.