0

I have a pseudo RxSwift implementation of a repository pattern that supports offline and remote operations. I'm trying to migrate it from using our homegrown implementation of Dynamic<T> to using promises from the PromiseKit library; the code I'm trying to migrate looks like the following:

class Repository {
   private var realmRepo: RealmRepo!
   private var httpRepo: HTTPRepo!
   private var offlineCache: OfflineCache!

   func complete(step: Entity, options: Options) -> Dynamic<Entity> {
       let result = Dynamic<Entity>()

       realmRepo.complete(step: step, options: options).do(onNext: {[result] value in
            guard let realmStep = value else {
                return result.complete(with: Error.unknown("realm result was `nil`"))
            }

            self.httpRepo.complete(step: realmStep, options: options).do(onNext: { value in

                result.complete(with: value)

            }).do(onError: { error in
                switch error {
                case HTTPRepo.Error.noNetworkConnection(let request):
                    try? self.offlineCache.add(object: createOfflineObject(request as! URLRequest))
                    result.complete(with: realmStep)
                default:
                    result.complete(with: error)
                }
            })
        }).do(onError: {[result] error in
            result.complete(with: error)
        })

        return result
}

The Dynamic<T> class looks like the following (implementation omitted):

class Dynamic<T> {
    func `do`(onNext: (T?) -> Void) -> Dynamic<T> // returns `self`
    func `do`(onError: (Error) -> Void) -> Dynamic<T> // returns `self`
    func complete(with: Error)
    func complete(with: T?)
}

I'm trying to rewrite the return values for the repository from Dynamic<Entity> to Promise<Entity>, using the PromiseKit library, I'm not sure how to replicate the following chain of events using promises:

  1. Attempt the Realm call, if it fails send an error to the returned dynamic object (or fail the promise)
  2. If the RealmRepo.complete call succeeded, then attempt the HTTPRepo.complete call.
  3. If the HTTPRepo.complete call succeeds then emit a "next" value to the dynamic (promise succeeds)
  4. If the HTTPRepo.complete call fails then catch the error which will have the failure reason and if it was a lack of network connection then perform another call to the OfflineCache.add method and resolve the Dynamic value with the result from the RealmRepo.complete call.

So far I've managed the following:

import PromiseKit 

// ... other code ...

class Repository {
   // ... repository fields ...

   func complete(step: Entity, options: Options) -> Promise<Entity> {
      return self.realmRepo.complete(step: step, options).then { 
          return self.httpRepo.complete(step: $0, options: options)
      }.catch { error in 
         switch error {
         case HTTPRepo.Error.noNetworkConnection(let request):
             try? self.offlineCache.add(object: createOfflineObject(request as! URLRequest))
         default:
             result.complete(with: error)
         }
      }
   }
}

Such code gives me a compile error in the catch block, I'm not even sure of how I'd handle or recover from the error that httpRepo would return if no connection is present.

Any help is really appreciated!

Tristian
  • 3,406
  • 6
  • 29
  • 47
  • Why would you do it with PromiseKit (3rd party) if Apple's own Combine framework is itself pseudo-RxSwift/RxCocoa? Just curious about the motivation. – Maxim Volgin Dec 18 '19 at 09:45
  • Promise kit is an improvement over completion callbacks, our app does not use RxSwift and we distribute to iOS versions lower than 13 which is the required version Toby able to use the Combine framework – Tristian Dec 18 '19 at 16:04

0 Answers0