Search code examples
iosswiftpromisepromisekit

PromiseKit 6.0 passing catched errors to caller


According to migration guide PromiseKit 6.x changed his policy about catch blocks. In PMK 4 catch returned the promise it was attached to. Now catch is a chain- terminator. I understand why these changes were made but...

In my codebase (connected with PK4) I take some advantages from that catch returns promise.

func loginAndSync(withServerAddress address: String, port: String) -> Promise<()> {
    synchronizationService.stopAllRunningSingleSynchronizations()

    return
        authorizationService.save(serverAddress: address, andPort: port)
            .then {
                self.synchronizationService.startSingleFullSynchronization()
            }
            .then {
                self.authorizationService.markAsServerSynced()
            }
            .catch { error in
                log.error(error)
                _ = self.authorizationService.markAsServerUnsynced()
            }
}

In this function I make some logic which is in some cases failable. In case of error catch block should make some logic but I want to also send to loginAndSync function`s caller a result of this promise (fulfill or reject). Above function can be called for example by ViewController and in ViewController I want to show for example Error or Success Dialog.

This is a reason where I need a two catches for one Promise-chain. One for authorizationService and one for UI.

Is there any workaround in PromiseKit6 to achieve this?

Edit

I found two solutions (workarounds). I have created two answers to separate them. Future readers can decide which one is better or provide new one.


Solution

  • I found (IMO) better workaround. PromiseKit6 introduces very handy method tap. I have tried use it to resolve my problem.

    func loginAndSync(withServerAddress address: String, port: String) -> Promise<()> {
        synchronizationService.stopAllRunningSingleSynchronizations()
    
        return authorizationService.save(serverAddress: address, andPort: port)
            .then {
                self.synchronizationService.startSingleFullSynchronization()
            }
            .then {
                self.authorizationService.markAsServerSynced()
            }
            .tap { result in
                switch result {
                case .rejected(let error):
                    log.error(error)
                    _ = self.authorizationService.markAsServerUnsynced()
                default: break
                }
            }
            .done {
                self.coordinatorDelegate?.handleLoginServerSuccess()
            }
    }
    

    For doc:

    tap feeds you the current Result for the chain, so is called if the chain is succeeding or if it is failing

    So I can provide custom error handling for current state of Promise whit terminating the chain. Promise can be send to sender where I can do another tap or terminate chain by catch.