8

This used to work but with version 6 of PromiseKit this...

func checkIn(request: CheckinRequest) -> Promise<CheckinResponse> {
    let p = checkinService.checkIn(request: request)
        .then { r -> Promise<CheckinResponse> in
            return .value(r)
        }.catch { e in

        }
    return p
}

... gives ...

Cannot convert return expression of type 'PMKFinalizer' to return type 'Promise'

How can I add a catch block and continue to return the chain to the calling function?

Roman Podymov
  • 4,168
  • 4
  • 30
  • 57
Ian Warburton
  • 15,170
  • 23
  • 107
  • 189

2 Answers2

5

You just need to remove the catch block as below,

func checkIn(request: CheckinRequest) -> Promise<CheckinResponse> {
     let p = checkinService.checkIn(request: request)
          .then { r -> Promise<CheckinResponse> in
              return .value(r)
        }
      return p
}

Using the catching block here is irrelevant as the error should be handled by the callee.


Guarantee class is a wrapper class to make discardable result calls. So we can create a method that will process the promise so that we will just use the .done callback to use that result as below,

extension Promise {

    func result() -> Guarantee<T> {
        return Guarantee<T>(resolver: { [weak self] (body) in
            self?.done({ (result) in
                body(result)
            }).catch({ (error) in
                print(error)
            })
        })
    }
}

Now you can simply do,

let request = CheckinRequest()
checkinService.checkIn(request: request).result().done { response in
    // Check in response
}

You can still use chaining for multiple promises as below,

checkinService.checkIn(request: request).result().then { (result) -> Promise<Bool> in
        // Use reuslt
        return .value(true)
    }.done { bool in
        print(bool)
    }.catch { (e) in
        print(e)
}
Kamran
  • 14,987
  • 4
  • 33
  • 51
  • 1
    But then I have to repeat the catch block for every callee. – Ian Warburton Dec 13 '18 at 22:21
  • Yes, that’s how you can handle a promise. My guess is you want a method where you can tell the result type and get that result only. So for that you need to create a generic method with completion block and no return type of Promise. Anti-promise but this is what we needed. Call this completion in the .done callback of Promise and handle the error in catch block. Second option is to extend Promise by introducing a method that has discardableResult on the result to mute the catching block warning on every call. – Kamran Dec 13 '18 at 22:31
  • 1
    @IanWarburton I updated the answer with one helper method. This could be helpful. – Kamran Dec 14 '18 at 00:38
3

Everything you need to know is described here, just scroll to the .catch{ /*…*/ }.finally section.

You can use this solution if you want to keep the same functionality:

func checkIn(request: CheckinRequest) -> Promise<CheckinResponse> {
    let p = checkinService.checkIn(request: request).then { r -> Promise<CheckinResponse> in
        return .value(r)
    }
    p.catch { e in

    }
    return p
}

However, it is not recommended to use this pattern. catch is a chain terminator. Therefore, you should refactor your code. Check Kamran's answer for inspiration.

Roman Podymov
  • 4,168
  • 4
  • 30
  • 57