0

My promise chain is broken (maybe deallocated) before it's resolved. This happens (so far ONLY) when I make Alamofire request fail due to host trust evaluation -> forcing evaluation to fail which results in -999 cancelled etc).

Setup is rather simple:

APIRequest:

func start(_ onSuccess:@escaping SuccessBlock, onError:@escaping ErrorBlock) {

    do {
        try executeRequest { dataResponse in
            self.onSuccess(dataResponse)
        }

    } catch {
        self.onError(error)
    }
}

where executeRequest() is:

self.manager.request(request).responseJSON(queue: self.queue) { (response) in

    completion(response)
}

Then, there is PromiseKit wrapper defined as APIRequest extension: (This wrapper callbacks are called correctly in either case)

func start() -> Promise<APIResponse> {

    return Promise<APIResponse> { resolver in

        self.start({ response in

            resolver.fulfill(response)

        }) { error in

            resolver.reject(error)
        }
    }
}

And finally, unit test calling the start promise (extension): ( flow never reaches this place in case of Alamofire failing )

request.start().done { response in

}.catch { error in
    // not called if request failed
}

Outcome: if request fails -> the extension promise wrapper (catch) block is called, but it's not propagated back to UnitTest promise block.

Simply replacing Alamofire request with mock implementation (which triggers some other error( makes all setup work as expected (Unit Test completes with catch block being called etc) ex:

DispatchQueue.global(qos: .default).asyncAfter(deadline: .now() + 2) {
    let result = Alamofire.Result { () -> Any in 
       return try JSONSerialization.data(withJSONObject: [:], options: .fragmentsAllowed)
  }
  completion(DataResponse<Any>(request: nil, response: nil, data: nil, result: result))
}

Is this something to do with Alamofire? I don't really see how else to handle the promises there ( they shouldn't deallocate anyways... Or is this bug in PromiseKit? Alamofire? I yet have to test this in the app itself ( maybe it's Unit test issue ... )

Looking at related issues -> i'm definitely resolving promises (fulfilling / rejecting) for any flow path.

I don't see how Alamofire request is different from DispatchAsync (where the latter works as expected).

Pawel Klapuch
  • 380
  • 4
  • 15

2 Answers2

0
func start(_ onSuccess:@escaping SuccessBlock, onError:@escaping ErrorBlock) {

    do {
        try executeRequest { dataResponse in
            onSuccess(dataResponse)
        }

    } catch {
        onError(error)
    }
}

You are using self.onSuccess that means its not function parameter block but instance block thats why its not going back to block from you are calling start.

ketaki Damale
  • 634
  • 7
  • 25
  • I did say above that completion blocks are called correctly. The code I posted is simplified so please accept my apologies if it was confusing. – Pawel Klapuch Dec 13 '19 at 13:29
  • good i focus on this statement **Outcome: if request fails -> the extension promise wrapper (catch) block is called, but it's not propagated back to UnitTest promise block.** – Muhammad Jabbar Dec 13 '19 at 13:37
0

I was only 10 mins short of finding the answer... Problem is also described here: https://github.com/mxcl/PromiseKit/issues/686

Issue is that '-999 cancelled' error is not treated as 'Error' by PromiseKit. Solution is to use "catch(policy: .allErrors)" - then catch block is called as expected.

Pawel Klapuch
  • 380
  • 4
  • 15