3

I am trying to add a check against the response status code in the event my remote service returns a 401.

I have am trying to use the PromiseKit URLSession extension.

Imagine I have something basic such as

        return firstly {
            URLSession.shared.dataTask(.promise, with: request)
        }.compactMap {
            try JSONDecoder().decode(T.self, from: $0.data)
        }

What I would like to do is add a check against the response state code, so I may throw an error and execute some further steps.

Something like

        return firstly {
            URLSession.shared.dataTask(.promise, with: request)
        }.map { session  in
            if (session.response as? HTTPURLResponse)?.statusCode == 401 {
                // throw a custom error here
                // something like
                // throw TokenProviderError.unauthorized
            }

            return session.data

        }.compactMap {
            try JSONDecoder().decode(T.self, from: $0)
        }.catch { error in
            // check the error thrown here and do something
        }

This has an exception

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

Is it possible to introduce something like retryWhen which will allow me to catch an errors and check?

Teddy K
  • 820
  • 1
  • 6
  • 17

2 Answers2

1

I was trying to achieve exactly the same thing as you did, I think the retrywhen feature you were looking for is called recover, you can find it in GitHub PromiseKit Document

  • Please note the following code did not go through the editor, may not compile, I'm only using this to demonstrate the flow.
return firstly {
            URLSession.shared.dataTask(.promise, with: request)
        }.map { session  in
            if (session.response as? HTTPURLResponse)?.statusCode == 401 {
                throw TokenProviderError.unauthorized
            }
            return session.data
        }.compactMap {
            try JSONDecoder().decode(T.self, from: $0)
        }
        // above is a copy of your code
        .recover { error in // instead of .catch, use .recover here
            guard let err = error as? TokenProviderError,
                  err == .unauthorized 
                  else { throw error } // we only care 401 error here, other error will be handled in caller's .catch

            // in my case, the reason for 401 error is due to an expired access token, so I will try to use refresh token to get a valid access token and make the same request again
            return firstly {
                loginWithRefreshToken(token: myRefreshToken)
            }.then { loginRes -> Promise<(data: Data, response: URLResponse)> in
                // loginRes contains new access token, set it to request "Authorization" header, and make the same request again
                request.setValue(loginRes.accessToken, forHTTPHeaderField: "Authorization")
                return URLSession.shared.dataTask(.promise, with: request)
            }.map { session -> Data in
                return session.data
            }.compactMap { data -> Promise<T> in
                try JSONDecoder().decode(T.self, from: data)
            }
        }

Maybe it's a bit late for the answer, but I hope it can help some coders who are new to PromiseKit like myself.

Daniel Hu
  • 423
  • 4
  • 11
0

As .catch doesn't return anything but you want to return a Promise<T> so you have to remove .catch here as,

return firstly {
    URLSession.shared.dataTask(.promise, with: request)
}.map { session  in
    if (session.response as? HTTPURLResponse)?.statusCode == 401 {
        // throw a custom error here
        // something like
        // throw TokenProviderError.unauthorized
    }

    return session.data

}.compactMap {
    try JSONDecoder().decode(T.self, from: $0)
}

.catch will be implemented by the callee.


OR, may be you are looking something like,

func test() -> Promise<Any> {
    return firstly(execute: { () -> Promise<String> in
        return .value("bhai")
    }).map({ string -> Promise<Double?> in
        return .value(0.1)
    }).compactMap({ double -> NSError in
        return NSError(domain: "", code: 0, userInfo: nil)
    })
}
Kamran
  • 14,987
  • 4
  • 33
  • 51