ishkawa/APIKit

Question about custom interception/retry functionality

Closed this issue · 9 comments

Hey there,

I have a specific requirement for my app that I'm working on. The API web service uses JSON Web Tokens. It requires me to pass in a valid x-session-token header for each protected API endpoint.

These session tokens expire every 24h, which means I need to reauthenticate and retry my existing request. Is that currently possible with APIKit? I've seen some PRs about interceptors for version 2.0 but I'm not sure if that's what I need.

Here's an example of what I mean:
Let's say I need to call the GET /user endpoint of my API and it requires me to pass in a valid x-session-token in the header. If the token has expired, I will get an error response, something like this:

success: false, error: {
    code = E1100;
    hint = Token;
    message = "Session token has expired.";
}

Now whenever I get an error response with the error.code == "E1100", what I want to do is, automatically call the POST /authenticate_session endpoint, get the new session token and automatically retry the GET /user request with the updated x-session-token header.

Can you point me in the right direction of how I could best implement this with APIKit?

I'm currently using a different API library for my app and it's proving to be a pain (I'm literally at the point of where I'm trying to fight and go against how the framework was intended to work). Before I start implementing my API logic from scratch, I'm exploring my options and would like to research if I could accomplish this functionality requirement using APIKit (or any other open source library).

Thanks for your help.

Hi jyounus,

APIKit does not have retry functionality because it aims at providing type-safe single request. IMO, rich functionality for multiple request makes this library complex, so I think combining multiple request is responsibility of developers.

Next, let's think about your use case. To implement auto-retrying, wrapping sendRequest in a subclass is easiest. Here's example of implementation:

class CustomSession: Session {
    override func sendRequest<T: RequestType>(request: T, handler: (Result<T.Response, APIError>) -> Void = {r in}) -> NSURLSessionDataTask? {
        // 1. send request
        return super.sendRequest(request) { [weak self] result in
            // 2. check the error
            if case .Failure(let error) = result where /* check if the error is E1101 */ {
                // 3. refresh token if the error is E1101
                self?.refreshTokenWithHandler { [weak self] refreshedToken in
                    // 4. retry reqeust
                    self?.sendRequest(request, handler: handler)
                }
                return
            }

            handler(result)
        }
    }

    private func refreshTokenWithHandler(handler: String -> Void) {
        ...
    }
}

Note that Session.sharedSession is not a CustomSession.

Hey ishkawa,

Thanks for the reply! I will try it out and get back to you if I have any issues.

Regarding Session.sharedSession, any chance you could change "static" into "class" for your variables/methods? In theory, this should allow me to subclass Session and override the sharedSession property.

Sounds good 👍 I'll work on this tomorrow.

Unlike static property, class property cannot be stored property, so overriding this property will be something like this:

class CustomSession: Session {
    private static var privateSharedSession: Session = ...

    override class var sharedSession: Session {
        return privateSharedSession
    }
}

I expect class property supports stored property in the future version of Swift, because the compiler says:

Class stored properties not yet supported in classes

Ah okay, fair enough. No worries, for now I've just duplicated the method and named it something else in my subclass, so it's working fine for me. Obviously not ideal, but that can be improved whenever Swift supports this feature. :)

This change has been released in 1.4.0!

Nice one! I'm currently using your 2.x beta branch, but I will switch to 1.4 and give it a try!

Thanks man, appreciate your efforts on this library! :D

Shipped 2.x beta 4 yesterday. Now, this change is also available on 2.x branch!

Awesome! Thanks :D

@ishkawa

How can I implement retry ?
That implement does not work now....

Regards,