Combine sink:忽略receiveValue,只需要completion

Combine sink: ignore receiveValue, only completion is needed

考虑以下代码:

        CurrentValueSubject<Void, Error>(())
            .eraseToAnyPublisher()
            .sink { completion in

                switch completion {
                case .failure(let error):
                    print(error)
                    print("FAILURE")
                case .finished:
                    print("SUCCESS")
                }
            } receiveValue: { value in
                // this should be ignored
            }

只需查看 CurrentValueSubject 初始值设定项,很明显该值不需要/无关紧要。

我正在使用这个特定的发布者发出异步网络请求,该请求可能通过也可能失败。

由于我对这个发布者返回的值不感兴趣(有none),我怎样才能摆脱receiveValue闭包?

理想情况下,调用站点代码应如下所示:

        CurrentValueSubject<Void, Error>(())
            .eraseToAnyPublisher()
            .sink { completion in

                switch completion {
                case .failure(let error):
                    print(error)
                    print("FAILURE")
                case .finished:
                    print("SUCCESS ")
                }
            }

也有可能我应该使用不同于 AnyPublisher 的东西,所以如果 API 更符合目的,请随意提出/重写它。

我能找到的最接近的解决方案是 ignoreOutput,但它仍然是 returns 一个值。

CurrentValueSubject 似乎是一个令人困惑的选择,因为当您第一次订阅它时,它会发送一个初始值(Void)。

您可以使用 Future 来减少歧义,它会在完成后发送唯一的值。

为了避免必须接收您不关心的值,您可以翻转情况并使用 Result<Void, Error> 的输出类型和 Never 的失败类型。当处理你的网络请求时,你可以用 .failure(error).success(()) 履行承诺,并在 sink:

中处理它
let pub = Future<Result<Void, Error>, Never> {
    promise in
    // Do something asynchronous
    DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
        promise(.success(.success(())))
        //or
        //promise(.success(.failure(error)))
    }
}.eraseToAnyPublisher()

// somewhere else...
pub.sink {
    switch [=10=] {
    case .failure(let error):
        print("Whoops \(error)")
    case .success:
        print("Yay")
    }
}

您正在将链条一端的丑陋代码换成另一端的丑陋代码,但如果它隐藏在 AnyPublisher 后面并且您关心正确使用,这似乎是可行的方法.消费者可以通过查看输出和错误类型准确地看到预期的结果,而不必在单独的闭包中处理它们。

您只需完成即可声明另一个接收器:

extension CurrentValueSubject where Output == Void {
    
    func sink(receiveCompletion: @escaping ((Subscribers.Completion<Failure>) -> Void)) -> AnyCancellable {
        sink(receiveCompletion: receiveCompletion, receiveValue: {})
    }
}