'How to write auto-toggle Publisher operator?

because of a requirement to auto remove Text after its appearance in 2 seconds, I want to write this operator but still cannot figure out the best way to implement.

enter image description here

We can put a timespan parameter and get a new stream, here are two interfaces need to implement

extension Publisher {
    func reset<T, S: Scheduler>(
        after: S.SchedulerTimeType.Stride,
        on scheduler: S
    ) -> AnyPublisher<T, Failure> where Output == Optional<T> {
        // implementation
    }
    
    func autoToggle<S: Scheduler>(
        after: S.SchedulerTimeType.Stride,
        on scheduler: S
    ) -> AnyPublisher<Bool, Failure> {
        // implementation
    }
}

Really appreciate your help.



Solution 1:[1]

I assume you want something that doesn't turn on for more than 2 minutes.

The key operator will be something like this

$autoToggle
    .filter { $0 } // only take True signal
    .debounce(for: .seconds(2), scheduler: RunLoop.main) // for 2 second delay
    .map { !$0 } // turn it to False

I've created a sample so you can test and play with it

class ToggleTestViewMoel: ObservableObject {
    private var resetSubject = PassthroughSubject<Bool, Never>()
    @Published var autoToggle: Bool = false
    
    init() {
        let autoTurnOff = $autoToggle
            .filter { $0 }
            .debounce(for: .seconds(2), scheduler: RunLoop.main)
            .map { !$0 }
        
        resetSubject.merge(with: autoTurnOff)
            .assign(to: &$autoToggle)
    }
    
    func setReset(value: Bool) { resetSubject.send(value) }
}

struct ToggleTestView: View {
    @StateObject var viewModel = ToggleTestViewMoel()
    var body: some View {
        List {
            Button("Turn On") { viewModel.setReset(value: true) }
            Button("Turn Off") { viewModel.setReset(value: false) }
            Toggle("Auto Toggle", isOn: $viewModel.autoToggle)
        }
    }
}

struct TestOtherTestViews_Previews: PreviewProvider {
    static var previews: some View {
        ToggleTestView()
    }
}

enter image description here

UPDATE

If time interval between 2 signals from resetSubject always more than 2 seconds, you can replace

.debounce(for: .seconds(2), scheduler: RunLoop.main)

with

.delay(for: .seconds(2), scheduler: RunLoop.main, options: .none)

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1