2021-01-27

Combine之Publishers

[图片上传中...(image-a4764a-1611739910254-2)]

Publishers处于pipline的最上游,它的主要作用是发送数据,本文将介绍Combine中的Publishers。

[TOC]

Just

[图片上传中...(image-6c3ea0-1611739910254-1)]

Just可以算是最简单的publisher了,它发送数据的方式相当于透传数据,最常用的场景是配合flatMap:

class JustViewObservableObject: ObservableObject {
    var cancellable: AnyCancellable?

    struct Student: Decodable {
        let name: String
    }

    let json = """
    [{
    "name": "小明"
    },
    {
    "name": "小红"
    },
    {
    "name": "李雷"
    }]
    """

    init() {
        let publisher = PassthroughSubject()

        cancellable = publisher
            .flatMap { value in
                Just(value.data(using: .utf8)!)
                    .decode(type: [Student].self, decoder: JSONDecoder())
                    .catch { _ in
                        Just([Student(name: "无名氏")])
                    }
            }
            .sink(receiveCompletion: { _ in
                print("结束了")
            }, receiveValue: { someValue in
                print(someValue)
            })

        publisher.send(json)
    }
}
复制代码

输出如下:

[MCMarbleDiagramSwiftUI.JustViewObservableObject.Student(name: "小明"), 
 MCMarbleDiagramSwiftUI.JustViewObservableObject.Student(name: "小红"), 
 MCMarbleDiagramSwiftUI.JustViewObservableObject.Student(name: "李雷")]
复制代码

因为flatMap闭包要求的返回值必须是一个publisher,所以在上边的代码中,使用Just比较合适,它把json数据映射成模型数组。

在上边的catch中也用到了Just,目的是当发生错误时,返回一个默认的值,值得注意的是,catch同样要求返回一个publsiher。

Future

final public class Future : Publisher where Failure : Error {

    /// A type that represents a closure to invoke in the future, when an element or error is available.
    ///
    /// The promise closure receives one parameter: a `Result` that contains either a single element published by a ``Future``, or an error.
    public typealias Promise = (Result) -> Void

    /// Creates a publisher that invokes a promise closure when the publisher emits an element.
    ///
    /// - Parameter attemptToFulfill: A ``Future/Promise`` that the publisher invokes when the publisher emits an element or terminates with an error.
    public init(_ attemptToFulfill: @escaping (@escaping Future.Promise) -> Void)

    /// Attaches the specified subscriber to this publisher.
    ///
    /// Implementations of ``Publisher`` must implement this method.
    ///
    /// The provided implementation of ``Publisher/subscribe(_:)-4u8kn``calls this method.
    ///
    /// - Parameter subscriber: The subscriber to attach to this ``Publisher``, after which it can receive values.
    final public func receive(subscriber: S) where Output == S.Input, Failure == S.Failure, S : Subscriber
}
复制代码

Future是一个专门处理异步函数的publisher,通过分析上边的代码,我们发现,它使用一个闭包来初始化,该闭包的返回值是一个Result类型,也就是说,在闭包中,我们处理异步过程,异步处理完成后,需要返回这个Result类型。

我们使用一个实例来看一下:

class FutureViewObservableObject: ObservableObject {
    var cancellable: AnyCancellable?

    init() {
        cancellable = Future { promise in
            /// 模拟异步过程
            DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
                return promise(.success(true))
            }
        }
        .sink(receiveCompletion: {
            print($0)
        }, receiveValue: {
            print($0)
        })
    }
}
复制代码

在上边的代码中,使用DispatchQueue.main.asyncAfter模拟了一个异步的过程,本质上,这个异步过程可以是任何异步的过程,比如平时开发中的网络请求,或者某些系统的api等等。

使用Future把现有的异步过程加入pipline中,是一个不错的实践,但需要注意一点,Future会在创建后立刻被调用,而不是等待收到订阅者的请求才调用。要验证这个问题,我们可以修改一下代码:

cancellable = Future { promise in
    print("diaoyuong")
    /// 模拟异步过程
    DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
        return promise(.success(true))
    }
}
.print()
.sink(receiveCompletion: {
    print($0)
}, receiveValue: {
    print($0)
})
复制代码

只需要在闭包中增加一个打印即可,输出如下:

diaoyuong
receive subscription: (Future)
request unlimited
receive value: (true)
true
receive finished
finished
复制代码

可以看出,闭包确实在收到请求之前就已经调用了,要解决这个问题,需要在Future外边包装一个Deferred:

cancellable = Deferred {
    return Future { promise in
        /// 模拟异步过程
        DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
            return promise(.success(true))
        }
    }
}
.sink(receiveCompletion: {
    print($0)
}, receiveValue: {
    print($0)
})
复制代码

Empty

/// A publisher that never publishes any values, and optionally finishes immediately.
///
/// You can create a ”Never” publisher — one which never sends values and never finishes or fails — with the initializer `Empty(completeImmediately: false)`.
@available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *)
public struct Empty : Publisher, Equatable where Failure : Error {

    /// Creates an empty publisher.
    ///
    /// - Parameter completeImmediately: A Boolean value that indicates whether the publisher should immediately finish.
    public init(completeImmediately: Bool = true)

    /// Creates an empty publisher with the given completion behavior and output and failure types.
    ///
    /// Use this initializer to connect the empty publisher to subscribers or other publishers that have specific output and failure types.
    ///
    /// - Parameters:
    ///   - completeImmediately: A Boolean value that indicates whether the publisher should immediately finish.
    ///   - outputType: The output type exposed by this publisher.
    ///   - failureType: The failure type exposed by this publisher.
    public init(completeImmediately: Bool = true, outputType: Output.Type, failureType: Failure.Type)

    /// A Boolean value that indicates whether the publisher immediately sends a completion.
    ///
    /// If `true`, the publisher finishes immediately after sending a subscription to the subscriber. If `false`, it never completes.
    public let completeImmediately: Bool
}

复制代码

Empty不发送任何数据,并且可以选择是否立刻终止pipline,默认情况下,当创建了该publisher后,它就会立刻终止piline。

我们可以利用这个特性,当监听到错误后,立刻终止pipline。

class EmptyPublisherViewObservableObject: ObservableObject {
    var cancellable: AnyCancellable?

    init() {
        enum MyError: Error {
            case custom
        }

        let publisher = PassthroughSubject()

        cancellable = publisher
            .catch { _ in
                Empty()
            }
            .sink(receiveCompletion: {
                print($0)
            }, receiveValue: {
                print($0)
            })

        publisher.send(1)
        publisher.send(completion: Subscribers.Completion.failure(MyError.custom))
    }
}
复制代码

上边的代码使用catch监听错误,一旦错误发生,就发送一个Emptypublisher,利用它立刻结束pipline的特性来终止pipline,它和.replaceError(with: 0)不同的地方在于后者不会终止pipline,会把错误替换成默认的值。

Fail

class FailViewObservableObject: ObservableObject {
    var cancellable: AnyCancellable?

    init() {
        enum MyError: Error {
            case custom
        }

        cancellable = Fail(error: MyError.custom)
            .sink(receiveCompletion: {
                print($0)
            }, receiveValue: {
                print($0)
            })

    }
}
复制代码

FailEmpty都能立刻终止pipline,但Fail使用的场景并不多,这里就不做更多介绍了。

Publishers.Sequence

class SequenceViewObservableObject: ObservableObject {
    var cancellable: AnyCancellable?

    init() {
        let list: Array = [1, 2, 3]
        let set: Set = ["1", "2", "3"]
        let dict: Dictionary = ["name": "张三", "age": "20"]
        let str: String = "你好吗"

        cancellable = list.publisher
            .sink(receiveCompletion: {
                print($0)
            }, receiveValue: {
                print($0)
            })
    }
}
复制代码

Publishers.Sequence解决了像这种集合数据类型的数据流问题,凡是实现了Sequence协议的对象,都可以调用.publisher来自动创建一个publisher,然后就可以使用Combine中的所有功能。

在真实开发中,下边四种类型是最常用的:

  • Array
  • Set
  • Dictionay
  • String

前三个没啥好说的,我们看看String类型的例子:

cancellable = "你好吗".publisher
    .sink(receiveCompletion: {
        print($0)
    }, receiveValue: {
        print($0)
    })
复制代码

打印结果:

你
好
吗
finished
复制代码

有意思吧?字符串本质上就是字符的集合,使用上边的代码,轻松实现字符传输。

Record

/// A publisher that allows for recording a series of inputs and a completion, for later playback to each subscriber.
@available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *)
public struct Record : Publisher where Failure : Error {
    /// Creates a publisher to interactively record a series of outputs and a completion.
    ///
    /// - Parameter record: A recording instance that can be retrieved after completion to create new record publishers to replay the recording.
    public init(record: (inout Record.Recording) -> Void)

    /// Creates a record publisher from an existing recording.
    ///
    /// - Parameter recording: A previously-recorded recording of published elements and a completion.
    public init(recording: Record.Recording)

    /// Creates a record publisher to publish the provided elements, followed by the provided completion value.
    ///
    /// - Parameters:
    ///   - output: An array of output elements to publish.
    ///   - completion: The completion value with which to end publishing.
    public init(output: [Output], completion: Subscribers.Completion)
}
复制代码

Record其实是一个非常强大且有用的publisher,强大在于它可以编码和解码,就像它的名字一样,可以被保存,解析,传递。核心思想是先把数据保存起来,当收到订阅后再发送数据。

从上边的代码可以看出,他有3个初始化方法:

  1. 使用record: (inout Record.Recording) -> Void闭包初始化
let recordPublisher = Record { recording in
    recording.receive("你")
    recording.receive("好")
    recording.receive("吗")
    recording.receive(completion: Subscribers.Completion.finished)
}
复制代码
  1. 使用recording: Record.Recording初始化,也就是传入一个Recording类型的实例
var recording = Record.Recording()
recording.receive("你")
recording.receive("好")
recording.receive("吗")
recording.receive(completion: Subscribers.Completion.finished)
let recordPublisher = Record(recording: recording)
复制代码
  1. 使用output: [Output], completion: Subscribers.Completion参数初始化
let recordPublisher = Record(output: ["你", "好", "吗"], completion: Subscribers.Completion.finished)
复制代码

上边这3种初始化方法的效果都是一样的,我个人更倾向于第1种,感觉它的可读性更好一点。初始化成功后,它就跟Publishers.Sequence很像了

 cancellable = recordPublisher
            .sink(receiveCompletion: {
                print($0)
            }, receiveValue: {
                print($0)
            })
复制代码

打印结果:

你
好
吗
finished
复制代码

重点来了,上边说过,它是支持编解码的,什么意思呢? 就是我们可以把事先设定好的publisher模型保存起来,比如以JSON保存,在任何地方都可以从JSON解析成Record

编码的例子:

let jsonEncoder = JSONEncoder()
let jsonEncoded = try? jsonEncoder.encode(recordPublisher)
if let jsonData = jsonEncoded {
    let jsonStr = String(data: jsonData, encoding: .utf8)
    print(jsonStr ?? "编码错误")
}
复制代码
{"recording":{"completion":{"success":true},"output":["你","好","吗"]}}
复制代码

解码的例子:

let jsonDecoder = JSONDecoder()
let jsonDecoded = try? jsonDecoder.decode(Record.self, from: jsonEncoded!)
if let record = jsonDecoded {
    print(record)
}
复制代码
Record(recording: Combine.Record.Recording(state: Combine.Record.Recording.State.complete, output: ["你", "好", "吗"], completion: Combine.Subscribers.Completion.finished))
复制代码

Deferred

/// A publisher that awaits subscription before running the supplied closure to create a publisher for the new subscriber.
@available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *)
public struct Deferred : Publisher where DeferredPublisher : Publisher {
reatePublisher: () -> DeferredPublisher

    /// Creates a deferred publisher.
    ///
    /// - Parameter createPublisher: The closure to execute when calling `subscribe(_:)`.
    public init(createPublisher: @escaping () -> DeferredPublisher)
}
复制代码

简而言之,Deferred的核心思想就是当收到订阅后才创建publisher,从上边的代码中,可以看出,它创建publisher是通过初始化函数的一个闭包参数:

cancellable = Deferred {
    return Future { promise in
        /// 模拟异步过程
        DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
            return promise(.success(true))
        }
    }
}
.sink(receiveCompletion: {
    print($0)
}, receiveValue: {
    print($0)
})
复制代码

DeferredFuture是黄金搭档,Future不管有没有订阅者都会立刻执行,配合Deferred就完美实现了把任何异步问题添加到pipline中。

还有一点,对于比较耗费性能的publisher,也可以使用Deferred包装,只有当收到订阅后才会初始化。

MakeConnectable

class MakeConnectableViewObservableObject: ObservableObject {
    var cancellable1: AnyCancellable?
    var cancellable2: AnyCancellable?

    var cancellable: Cancellable?

    init() {
        let publisher = Just("Hello, world")
            .share()
            .makeConnectable()

        cancellable1 = publisher
            .sink(receiveCompletion: {
                print("Stream 1 received: \($0)")
            }, receiveValue: {
                print("Stream 1 received: \($0)")
            })

        DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
            self.cancellable2 = publisher
                .sink(receiveCompletion: {
                    print("Stream 2 received: \($0)")
                }, receiveValue: {
                    print("Stream 2 received: \($0)")
                })
        }

        DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
            self.cancellable = publisher.connect()
        }
    }
}
复制代码

按照publisher正常的生命周期,一旦收到订阅就会立刻建立连接,而在某些场景下,我们需要等待符合某个条件后再建立连接。上边的代码就是一个例子,由于Just调用了.share(),所以我们希望当所有的订阅都处理完成后再建立连接,这个时候,Just变成Connectable就很有必要。

很简单,publisher只需要调用.makeConnectable()就可以成为ConnectablePublisher。成为ConnectablePublisher后就可以调用下边两个方法:

  • connect
  • autoconnect

在上边的例子中,我们等待pipline建立完成后,调用了self.cancellable = publisher.connect(),从而开启了整个pipline。这是最常用的一个示例。

let publisher = Just("Hello, world")
    .share()
    .makeConnectable()
    .autoconnect()
复制代码

如果在.makeConnectable()之后,紧接着调用了.autoconnect()就相当于publisher没做任何事情,在真实的开发中,一定不要这么写代码。在下边的Timer中,我们还会用到这两个特性。

@Published

class PublishedViewObservableObject: ObservableObject {
    @Published var text: String = ""

    var cancellable: AnyCancellable?

    init() {
        cancellable = $text
            .sink(receiveValue: {
                print($0)
            })

        text = "hello, world"
    }
}
复制代码

@Published通常用在SwiftUI中的ObservableObject模型中,如果View依赖了该属性,当该属性改变时,View自动更新。

大家仔细看上边的代码,当我们调用$text的时候,它返回了一个publisher,这是为什么呢? 我们看看Published的定义:

@propertyWrapper public struct Published {

    /// A publisher for properties marked with the `@Published` attribute.
    public struct Publisher : Publisher {

        /// The kind of values published by this publisher.
        public typealias Output = Value

        /// The kind of errors this publisher might publish.
        ///
        /// Use `Never` if this `Publisher` does not publish errors.
        public typealias Failure = Never

        /// Attaches the specified subscriber to this publisher.
        ///
        /// Implementations of ``Publisher`` must implement this method.
        ///
        /// The provided implementation of ``Publisher/subscribe(_:)-4u8kn``calls this method.
        ///
        /// - Parameter subscriber: The subscriber to attach to this ``Publisher``, after which it can receive values.
        public func receive(subscriber: S) where Value == S.Input, S : Subscriber, S.Failure == Published.Publisher.Failure
    }

    /// The property for which this instance exposes a publisher.
    ///
    /// The ``Published/projectedValue`` is the property accessed with the `$` operator.
    public var projectedValue: Published.Publisher { mutating get set }
}
复制代码

@propertyWrapper我们后续会有专门的文章讲解,Published内部有一个属性projectedValue,它就是一个publisher,我们可以用$符号访问。

Published并不是SwiftUI中才能用,它是Combine的特性,因此我们可以在任何class中使用:

class MyClass {
    @Published var text: String = ""

    var cancellable: AnyCancellable?

    init() {
        cancellable = $text
            .sink(receiveValue: {
                print($0)
            })

        text = "hello, MyClass"
    }
}
复制代码

NotificationCenter

extension Notification.Name {
    static let myCustomNotification = Notification.Name("myCustomNotification")
}

class NotificationCenterPublisherViewObservableObject: ObservableObject {
    var cancellable: AnyCancellable?

    init() {
        cancellable = NotificationCenter.default
            .publisher(for: .myCustomNotification)
            .sink(receiveCompletion: {
                print($0)
            }, receiveValue: {
                print($0)
            })

        NotificationCenter.default.post(name: .myCustomNotification, object: nil)
    }
}

/// Prints:
/// name = myCustomNotification, object = nil, userInfo = nil
复制代码

Combine也为NotificationCenter提供了publisher的能力,上边的代码已经给出了很明确的解释,在这里就不做更多解释了。

Timer

extension Timer {

    /// Returns a publisher that repeatedly emits the current date on the given interval.
    ///
    /// - Parameters:
    ///   - interval: The time interval on which to publish events. For example, a value of `0.5` publishes an event approximately every half-second.
    ///   - tolerance: The allowed timing variance when emitting events. Defaults to `nil`, which allows any variance.
    ///   - runLoop: The run loop on which the timer runs.
    ///   - mode: The run loop mode in which to run the timer.
    ///   - options: Scheduler options passed to the timer. Defaults to `nil`.
    /// - Returns: A publisher that repeatedly emits the current date on the given interval.
    public static func publish(every interval: TimeInterval, tolerance: TimeInterval? = nil, on runLoop: RunLoop, in mode: RunLoop.Mode, options: RunLoop.SchedulerOptions? = nil) -> Timer.TimerPublisher

    /// A publisher that repeatedly emits the current date on a given interval.
    final public class TimerPublisher : ConnectablePublisher {

        /// The kind of values published by this publisher.
        public typealias Output = Date

        /// The kind of errors this publisher might publish.
        ///
        /// Use `Never` if this `Publisher` does not publish errors.
        public typealias Failure = Never

        final public let interval: TimeInterval

        final public let tolerance: TimeInterval?

        final public let runLoop: RunLoop

        final public let mode: RunLoop.Mode

        final public let options: RunLoop.SchedulerOptions?

        /// Creates a publisher that repeatedly emits the current date on the given interval.
        ///
        /// - Parameters:
        ///   - interval: The interval on which to publish events.
        ///   - tolerance: The allowed timing variance when emitting events. Defaults to `nil`, which allows any variance.
        ///   - runLoop: The run loop on which the timer runs.
        ///   - mode: The run loop mode in which to run the timer.
        ///   - options: Scheduler options passed to the timer. Defaults to `nil`.
        public init(interval: TimeInterval, tolerance: TimeInterval? = nil, runLoop: RunLoop, mode: RunLoop.Mode, options: RunLoop.SchedulerOptions? = nil)

        /// Connects to the publisher, allowing it to produce elements, and returns an instance with which to cancel publishing.
        ///
        /// - Returns: A ``Cancellable`` instance that you use to cancel publishing.
        final public func connect() -> Cancellable
    }
}
复制代码

Timer是比较适合作为publisher的,通常来说,我们之所以使用Timer,就是需要按照固定的时间间隔来接收数据。

从上边的代码可以看出,如果想为Foundation框架中的元素增加Combine功能,只需要写一个extension就可以了,对于Timer,为它绑定一个TimerPublisher

注意,TimerPublisher实现了ConnectablePublisher协议,因此它可以访问.autoconnect().connect()

如果我们想自动开启定时器,代码是这样的:

cancellable = Timer.publish(every: 1.0, on: .main, in: .common)
    .autoconnect()
    .sink(receiveCompletion: {
        print($0)
    }, receiveValue: {
        print($0)
    })
复制代码

如果想手动控制连接,代码是这样的:

class TimerViewObservableObject: ObservableObject {
    var cancellable: AnyCancellable?
    var timerPublisher = Timer.publish(every: 1.0, on: .main, in: .common)
    var connectable: Cancellable?

    @Published var count: Int = 1

    init() {
    }

    func startTimer() {
        cancellable = timerPublisher
            .sink(receiveCompletion: {
                print($0)
            }, receiveValue: { _ in
                self.count += 1
            })

        connectable = timerPublisher
            .connect()
    }

    func stopTimer() {
        connectable?.cancel()
    }
}
复制代码

注意,当调用了connectable?.cancel(),就终止了该pipline,要想重新启动,需要重新开启订阅流程。

效果如下:

[图片上传中...(image-8a3f4f-1611739910251-0)]

KeyValueObservingPublisher

从事iOS开发的同学一定对kvo比较熟悉,简而言之,我们可以监听某个对象中属性的变化,我们就不详细介绍了,可以在这里Using Key-Value Observing in Swift了解详细信息。

class MyObjectToObserve: NSObject {
    @objc dynamic var myDate = NSDate(timeIntervalSince1970: 0) // 1970
    func updateDate() {
        myDate = myDate.addingTimeInterval(Double(2 << 30)) // Adds about 68 years.
    }
}
复制代码
class MyObserver: NSObject {
    @objc var objectToObserve: MyObjectToObserve
    var observation: NSKeyValueObservation?

    init(object: MyObjectToObserve) {
        objectToObserve = object
        super.init()

        observation = observe(
            \.objectToObserve.myDate,
            options: [.old, .new]
        ) { object, change in
            print("myDate changed from: \(change.oldValue!), updated to: \(change.newValue!)")
        }
    }
}
复制代码

虽然在平时的编码中,我们很少用到kvo,但Combine也为kvo增加了扩展,用法如下:

final class MyKeyValueClass: NSObject {
    @objc dynamic var name: String = ""
    @objc dynamic var age: Int = 20
}

class KeyValueObservingPublisherViewObservableObject: ObservableObject {
    var cancellable: AnyCancellable?

    init() {
        let keyValueClass = MyKeyValueClass()

        cancellable = keyValueClass.publisher(for: \.name)
            .sink(receiveCompletion: {
                print($0)
            }, receiveValue: {
                print($0)
            })

        keyValueClass.name = "James"
    }
}
复制代码
  • 在objc的类中用@objc dynamic声明属性
  • 使用keyValueClass.publisher(for: \.name)创建publisher

DataTaskPublisher

extension URLSession {

    /// Returns a publisher that wraps a URL session data task for a given URL.
    ///
    /// The publisher publishes data when the task completes, or terminates if the task fails with an error.
    /// - Parameter url: The URL for which to create a data task.
    /// - Returns: A publisher that wraps a data task for the URL.
    public func dataTaskPublisher(for url: URL) -> URLSession.DataTaskPublisher

    /// Returns a publisher that wraps a URL session data task for a given URL request.
    ///
    /// The publisher publishes data when the task completes, or terminates if the task fails with an error.
    /// - Parameter request: The URL request for which to create a data task.
    /// - Returns: A publisher that wraps a data task for the URL request.
    public func dataTaskPublisher(for request: URLRequest) -> URLSession.DataTaskPublisher

    public struct DataTaskPublisher : Publisher {

        /// The kind of values published by this publisher.
        public typealias Output = (data: Data, response: URLResponse)

        /// The kind of errors this publisher might publish.
        ///
        /// Use `Never` if this `Publisher` does not publish errors.
        public typealias Failure = URLError

        public let request: URLRequest

        public let session: URLSession

        public init(request: URLRequest, session: URLSession)
    }
}
复制代码

URLSession.shared.dataTaskPublisher(for url: URL)是网络请求中使用最频繁的publisher,值得注意的有以下几点:

  • Output = (data: Data, response: URLResponse):它的输出类型是一个元组
  • Failure = URLError: 错误类型为URLError
  • 使用URLURLRequest创建网络请求

Result.Publisher

class ResultPublisherViewViewObservableObject: ObservableObject {
    var cancellable1: AnyCancellable?
    var cancellable2: AnyCancellable?

    init() {

        enum MyError: Error {
            case custom
        }

        let publisher = Result.Publisher(.success(1))
            .share()

        cancellable1 = publisher
            .sink(receiveCompletion: {
                print("Stream 1 received: \($0)")
            }, receiveValue: {
                print("Stream 1 received: \($0)")
            })

        cancellable2 = publisher
            .sink(receiveCompletion: {
                print("Stream 2 received: \($0)")
            }, receiveValue: {
                print("Stream 2 received: \($0)")
            })
    }
}

/// Prints:
/// Stream 1 received: 1
/// Stream 1 received: finished
/// Stream 2 received: finished
复制代码

Result.Publisher主要用于发送是否成功或失败事件,如果是.success事件,则收到请求后就发送数据,然后再结束pipline, 而.faulure立即结束pipline。

其实Result.PublisherJust很像,不同之处在于:

  • Result.Publisher可以发送数据+成功+失败
  • Just只能发送数据

你可能感兴趣的:(2021-01-27)