仅当它想完成而不发布任何值时才将元素附加到发布者

时间:2021-04-06 03:16:20

标签: swift combine

我有两个发布者,他们都可以发布一个值,或者完全不发布任何值。我正在合并两个发布者并比较它们的值并对这些值进行一些后处理并将它们保存到我的本地 CoreData。简化代码如下:

let p1 = ["1"]
    .publisher
    .map { Int($0) }

let p2 = ["2"]
    .publisher
    .map { Int($0) }

let p1p2 = p1.combineLatest(p2)
    .map { $0 == $1 }
    .sink { print($0) }

但是如果其中一个发布者没有发布任何值,我仍然需要将剩余的值保存到我的本地数据库中。问题是,如果发布者之一在没有触发值的情况下完成,则 combineLatest 运算符不会触发任何事件。我尝试了 prepend 和 append 运算符,但它们不能与任何条件结合使用,例如如果其中一个发布者在没有发布任何值的情况下完成。

例如,类似于:

let p1String: [String] = []
let p1 = p1String.publisher
    .map { Int($0) }
    .if(completionWithoutPublishing, perform: { prepend(nil) })

任何想法将不胜感激。

2 个答案:

答案 0 :(得分:2)

您可以制作执行此操作的自定义运算符。您只需要实现 receive(subscriber:)。为此,您可以将自定义逻辑添加到上游发布者(在您的情况下为 map),然后将订阅者参数附加到它。

struct IfEmpty<Upstream: Publisher>: Publisher {
    let upstream: Upstream
    let output: Output
    let handler: (() -> Void)?
    
    func receive<S>(subscriber: S) where S : Subscriber, Self.Failure == S.Failure, Self.Output == S.Input {
        var hasOutput = false
        upstream.handleEvents(receiveOutput: { (_) in
            hasOutput = true
        }, receiveCompletion: { (_) in
            if !hasOutput {
                subscriber.receive(output)
                handler?()
            }
        }).receive(subscriber: subscriber)

    }
    
    typealias Output = Upstream.Output
    
    typealias Failure = Upstream.Failure
}

extension Publisher {
    func ifEmpty(publish output: Output, andDo handler: (() -> Void)? = nil) -> IfEmpty<Self> {
        IfEmpty(upstream: self, output: output, handler: handler)
    }
}

示例:

let p1String: [String] = []
let p1 = p1String.publisher
    .map { Int($0) }
    .ifEmpty(publish: nil)
    .print()
    .sink(receiveValue: { _ in })
/*
receive subscription: (HandleEvents)
request unlimited
receive value: (nil)
receive finished
*/
let p1String: [String] = ["1", "2", "3"]
let p1 = p1String.publisher
    .map { Int($0) }
    .ifEmpty(publish: nil)
    .print()
    .sink(receiveValue: { _ in })
receive subscription: (HandleEvents)
request unlimited
receive value: (Optional(1))
receive value: (Optional(2))
receive value: (Optional(3))
receive finished
*/

答案 1 :(得分:2)

您正在寻找 replaceEmpty 运算符:

https://developer.apple.com/documentation/combine/publishers/zip4/replaceempty(with:)

https://www.apeth.com/UnderstandingCombine/operators/operatorsTransformersBlockers/operatorsreplaceempty.html

它按照您的描述执行:仅当上游完成而未发布时才发出其特殊值。