1

I am pretty new to reactive programming, and am using Bond with ReactiveKit in my app. I am now running into a problem that I can't quite figure out.

The problem is that on a certain page I have to upload a number of images, and when all images are uploaded, I want to do something.

This is the code I have so far:

func uploadImages(completionHandler: @escaping EmptyBlock) {
  let datas = profileImages.value.compactMap { $0?.jpegData(compressionQuality: 0.6) }

  let signals = datas.map { data in
    return Signal<String, Error> { observer in
      self.user.uploadImage(data: data) { result in
        switch result {
        case .success(let url):
          observer.receive(url)
          observer.receive(completion: .finished)
        case .failure(let error):
          observer.receive(completion: .failure(error))
        }
      }

      return SimpleDisposable(isDisposed: false)
    }
  }

  // What can I do with `signals`?
}

So I have a property profileImages, which is an Observable of an array of UIImage. When the uploadImages is executed, I want to upload all the images using a function user.uploadImage, and when all the uploads are completed, I want to call the completionHandler of the uploadImages function.

At this point I have an array of signals, but I can't figure out how to observe all of them, or a way to combine them into one signal?

Kevin Renskers
  • 5,156
  • 4
  • 47
  • 95

1 Answers1

0

I think it would help you if you broke the function up a bit:

extension User {
    func uploadImage(data: Data) -> Signal<String, Error> {
        return Signal { observer in
            self.uploadImage(data: data) { result in
                switch result {
                case .success(let url):
                    observer.receive(url)
                    observer.receive(completion: .finished)
                case .failure(let error):
                    observer.receive(completion: .failure(error))
                }
            }
            return SimpleDisposable(isDisposed: false)
        }
    }
}

The above will wrap an individual upload in a Signal. Then you can:

let user = self.user! // this is to avoid capturing `self` inside the map below.
let urlSignals = profileImages.value
    .compactMap { $0?.jpegData(compressionQuality: 0.6) }
    .map { user.uploadImage(data: $0) }
let urls = Signal(combiningLatest: urlSignals, combine: { $0 })

disposeBag += urls.observe(with: { event in
    switch event {
    case .next(let urls):
        print("all urls:", urls)
    case .failed(let error):
        print("an upload failed:", error.localizedDescription)
    case .completed:
        print("upload complete")
    }
})
Daniel T.
  • 32,821
  • 6
  • 50
  • 72