Swift Combine - 延迟发布者

9

简而言之

我想要延迟发布,但不知道如何将部分组合起来。

详细描述

我有一个 Publisher

let generator = PassthroughSubject<Bool, Never>()

并且想要以某种方式使用修饰符

.delay(for: 2, scheduler: RunLoop.main)

这样当我调用时

generator.send(true)

调用send()后两秒钟发送消息。

查看Publishers.Delay的文档使类型错误更清晰,但并不能帮助我找到正确的连接方式。

代码

import SwiftUI
import Combine

// Exists just to subscribe.
struct ContainedView : View {
    private let publisher: AnyPublisher<Bool, Never>
    init(_ publisher: AnyPublisher<Bool, Never> = Just(false).dropFirst().eraseToAnyPublisher()) {
        self.publisher = publisher
    }
    var body: some View {
        Rectangle().onReceive(publisher) { _ in print("Got it") }
    }
}

struct ContentView: View {
    let generator = PassthroughSubject<Bool, Never>()
                 // .delay(for: 2, scheduler: RunLoop.main)
                 // Putting it here doesn't work either.

    var body: some View {
        VStack {
            Button("Tap") {

                // Does not compile
                self.generator.delay(for: 2, scheduler: RunLoop.main).send(true)
                // Value of type 'Publishers.Delay<PassthroughSubject<Bool, Never>, RunLoop>' has no member 'send'
                // https://developer.apple.com/documentation/combine/publishers/delay

                // Does not compile
                self.generator.send(true).delay(for: 2, scheduler: RunLoop.main)
                // Value of tuple type '()' has no member 'delay'

                // Just a broken-up version of the first try.
                let delayed = self.generator.delay(for: 2, scheduler: RunLoop.main)
                delayed.send(true)

                // This, of course, builds and works.
                self.generator.send(true)
                print("Sent it")
            }

            ContainedView(generator.eraseToAnyPublisher())
            .frame(width: 300, height: 200)
        }
    }
}

3个回答

14

您可以使用发布者的防抖属性来延迟发布。

$yourProperty
    .debounce(for: 0.8, scheduler: RunLoop.main)
    .eraseToAnyPublisher()

14

.delay(for: 2, scheduler: RunLoop.main) 很可能是你需要的,但是为了完全理解问题,看一下你的订阅方式很关键。当使用一个subject和send()时,delay并不会延迟值的发送 - 这是命令式代码的链接,并且会在每次调用send时发送数据,通常针对一些已经存在的订阅。

虽然你在第一部分代码中有一个subscriber,但是没有一个subject将它们联系起来。

例如,如果你将:

Just(false).dropFirst().eraseToAnyPublisher()

更新为:

Just(false).dropFirst().eraseToAnyPublisher().delay(for: 2, scheduler: RunLoop.main)

那么print语句应该会在init()被调用后约2秒触发。取决于你想要实现什么,使用像onAppear这样的闭包触发器可能更有意义,让它调用subject.send(),然后你可以在发布者链中自己喜欢的位置延迟它,然后再订阅它。


谢谢@heckj,每次我使用send()时,我都觉得我的代码过于命令式和if-then-else,而不是基于状态的、立足于当下的。 - Andrew Duncan

2
let generator = PassthroughSubject<Bool, Never>()
let generated = generator.delay(for: 2, scheduler: RunLoop.main).sink { value in
    print(value.description + " " + Date().timeIntervalSinceReferenceDate.description)
}
print(Date().timeIntervalSinceReferenceDate.description)
generator.send(true)
generator.send(false)

输出

641453284.840604
true 641453286.841731
false 641453286.847715

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接