Search code examples
iosswiftrx-swift

Create Observable from Progress object


I'm trying to use the Google MLKitTranslate framework for offline translation in my app. I have written a function for downloading a language model.

public func downloadModel(forLanguage language: TranslateLanguage) -> Observable<Double> {
    let model = TranslateRemoteModel.translateRemoteModel(language: language)

    return Observable<Double>.create { _ -> Disposable in

      // Observe the download progress
      self.progress = ModelManager.modelManager().download(
        model,
        conditions: ModelDownloadConditions(
          allowsCellularAccess: true,
          allowsBackgroundDownloading: true
        )
      )

      return Disposables.create()
    }
  }

progress here has the Progress type.

However I want to create an Observable that sends out a .next event when I have an update in the .fractionComplete and a .completed event when .fractionComplete is 1.0.

I have tried using .rx.observe and .rx.observeWeakly (KVO) on the progress object but it didn't work.

So how can I create an observable from this Progress event? Help is much appreciated.


Solution

  • This should work:

    func downloadModel(forLanguage language: TranslateLanguage) -> Observable<Double> {
        let model = TranslateRemoteModel.translateRemoteModel(language: language)
        return Observable.create { observer -> Disposable in
            let progress = ModelManager.modelManager().download(
                model,
                conditions: ModelDownloadConditions(
                    allowsCellularAccess: true,
                    allowsBackgroundDownloading: true
                )
            )
    
            return progress.rx.observe(Double.self, "fractionCompleted")
                .compactMap { $0 }
                .takeUntil(.inclusive, predicate: { $0 >= 1.0 })
                .subscribe(observer)
        }
    }
    

    A couple of things to note. This is not a method, do not put it in a class. It is a free function. The progress object does not need to be retained by any class, the model manager will retain it and release it when it isn't needed anymore.

    If you are using the create(_:) operator, do not ignore the parameter being passed into your closure. If you do that you will not be able to emit anything.