Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Swift Combine .repeat

I'd like to create a repeat functionality that creates a loop in my code using Combine. I noticed that Combine does not have a repeat publisher via this great repo: https://github.com/freak4pc/rxswift-to-combine-cheatsheet. Heres the code that I wrote that works to repeat 2 states. How do I reduce this to something more readable or create my own repeat function?

toggleShouldDisplay = Just<Void>(())
  .delay(for: 2, scheduler:RunLoop.main)
  .map({ _ in
    self.shouldDisplay = true
    self.didChange.send(())
  })
  .delay(for: 2, scheduler: RunLoop.main)
  .map({ _ in
    self.shouldDisplay = false
    self.didChange.send(())
  })
  .setFailureType(to: NSError.self)
  .tryMap({ _ in
    throw NSError()
  })
  .retry(.max) // I might hit Int.max if I reduce the delays
  .sink(receiveValue: { _ in
    //Left empty
  })
like image 568
PaulWoodIII Avatar asked Mar 03 '23 17:03

PaulWoodIII


2 Answers

The .retry(_:) operator is really intended to be used for retrying operations that can fail, such as network requests. It sounds like you need a timer instead. Luckily, as of Xcode 11 beta 2, Apple has added Publisher support to the Timer class in Foundation.

One other comment about your implementation: I assume this code is used in a BindableObject because you are accessing didChange. Since didChange can be any kind of Publisher, why not use your shouldDisplay property as the Publisher?

final class MyModel: BindableObject {
    var didChange: CurrentValueSubject<Bool, Never> { shouldDisplaySubject }
    var shouldDisplay: Bool { shouldDisplaySubject.value }

    private let shouldDisplaySubject = CurrentValueSubject<Bool, Never>(false)
    private var cancellables: Set<AnyCancellable> = []

    init() {
        startTimer()
    }

    private func startTimer() {
        Timer.publish(every: 2, on: .main, in: .default)
            .autoconnect()
            .scan(false) { shouldDisplay, _ in
                !shouldDisplay
            }
            .assign(to: \.value, on: shouldDisplaySubject)
            .store(in: &cancellables)
    }
}
like image 171
dalton_c Avatar answered Mar 31 '23 09:03

dalton_c


You can use Timer.Publisher like this:

toggleShouldDisplay = Timer.publisher(every: 2, on: .main, in: .default)
  .autoconnect()
  .sink {
    self.shouldDisplay = !self.shouldDisplay
    self.didChange.send(())
  }

autconnect() lets the Timer start as soon as you subscribe using sink(_:).

like image 26
amgalan-b Avatar answered Mar 31 '23 10:03

amgalan-b