Search code examples
swiftswiftuiobservablerx-swiftcombine

RxSwift `ActivityIndicator` Functionality in Combine


I've been working with RxSwift for a few years now, and am starting to explore Combine with SwiftUI and am having some trouble trying to replicate some functionality from RxSwift in Combine.

On the RxSwift GitHub there is an example in a file called ActivityIndicator.swift.

Basic usage is as follows:

class Foo {
  let activityIndicator = ActivityIndicator()

  lazy var activity = activityIndicator.asDriver()

  var disposeBag = DisposeBag()

  func doSomething() {
    Observable
      .just("this is something")
      .trackActivity(activityIndicator)
      .subscribe()
      .disposed(by: disposeBag)
  }
}

What this does is allow you to then drive off of the activity driver and it will emit boolean values every time something subscribes or a subscription completes.

You can then directly drive something like a UIActivityIndicatorView's isAnimating property using RxCocoa.

I've been trying to figure out how to create something similar to this in Combine but am not having any luck.

Say I have a viewModel that looks like this:

class ViewModel: ObservableObject {
  @Published var isActive = false

  func doSomething() -> AnyPublisher<Void, Never> {
    Just(())
      .delay(for: 2.0, scheduler: RunLoop.main)
      .eraseToAnyPublisher()
  }
}

What I would like to do is create an operator for a Publisher that will function similarly to how the Rx operator worked where I can forward the events from the subscription through the chain, but change the isActive value every time something subscribes/completes/cancels.

In the SwiftUI View I would initiate the doSomething function and sink to it, while also being able to use the published isActive property to show/hide a ProgressView

Something similar to this:

struct SomeView: View {
  let viewModel = ViewModel()

  var body: some View {
    var cancelBag = Set<AnyCancellable>()

    VStack {
      Text("This is text")

      if viewModel.isActive {
        ProgressView()
      }
    }
    .onAppear(perform: {
      viewModel
        .doSomething()
        .sink()
        .store(in: &cancelBag)
    })
  }
}

Is there something that works like this already that I am just completely missing?

If not, how can I go about replicating the RxSwift functionality in Combine?

Thank you in advance for the help.


Solution

  • Looks like someone created a Combine version. I don't know if it has the same issue as discussed by @Daniel T. but it looks promising. https://github.com/duyquang91/ActivityIndicator