Swagger-codegen: Alamofire retrier (Swift)

Created on 6 Feb 2018  路  12Comments  路  Source: swagger-api/swagger-codegen

I've been using codegen generated code in my viewControllers in order to call our API's for a little while. Now, I need to implement some logic to refresh our token once I get a 401 error code.

If I modify AlamofireImplementations.swift's execute function and do "manager.retrier = MyRetrier()" after "let manager = createSessionManager()", I am able to catch a failed call, and retry it if needed.

However, I don't want to modify it there as it will get overwritten next time I run codegen.

If I subclass it, and override createSessionManager, what am I supposed to with it? I mean, none of the API classes generated by codegen will be using my subclass.

Please, how can I set a retrier for codegen's Alamofire?

Swagger-codegen 2.3.0

Swift Question

Most helpful comment

import UIKit
import Alamofire

class MACustomAlamofireRequestBuilderFactory: RequestBuilderFactory {

    func getNonDecodableBuilder<T>() -> RequestBuilder<T>.Type {
        return MACustomAlamofireRequestBuilder<T>.self
    }

    func getBuilder<T:Decodable>() -> RequestBuilder<T>.Type {
        return MACustomAlamofireDecodableRequestBuilder<T>.self
    }
}

class MACustomAlamofireRequestBuilder<T>: AlamofireRequestBuilder<T>   {

    open override func createSessionManager() -> Alamofire.SessionManager {

        let configuration = URLSessionConfiguration.default
        configuration.httpAdditionalHeaders = buildHeaders()

        let handler = MAAuthenticationManager()

        let manager = Alamofire.SessionManager(configuration: configuration)
        manager.adapter = handler
        manager.retrier = handler

        return manager
    }

}

class MACustomAlamofireDecodableRequestBuilder<T:Decodable>: AlamofireDecodableRequestBuilder<T>   {

    override func createSessionManager() -> Alamofire.SessionManager {

        let configuration = URLSessionConfiguration.default
        configuration.httpAdditionalHeaders = buildHeaders()

        let handler = MAAuthenticationManager()

        let manager = Alamofire.SessionManager(configuration: configuration)
        manager.adapter = handler
        manager.retrier = handler

        return manager
    }
}

All 12 comments

cc @jgavris @ehyche @Edubits @jaz-ah

@sadanro100 one question I have is should this happen in AlamofireImplementations or be something application specific that you do up in the controllers - i.e. handle errors up top and do a :
.catch { error -> Void in
HandleAndRetry401s(error)
since it's possible you don't always want to retry or what happens if you get an error again on the retry... maybe you'll have backoff policy if you happen to get multiple 401's in a row...
-the other temporary option is what I've done in AlamofireImplementations when I want to do something custom is just run the generator and always run a special script after that makes automatic fixes to the code w/ sed+awk to replace parts w/ my own customizations. not as ideal but a possibility so you don't have to worry about things getting overwritten.

@jaz-ah Thanks for the response.

From what I understand, Alamofire has the Adapter and the Retrier features built-in so that we can deal with failed requests.

AlamofireImplementations.swift's function createSessionManager is overridable so that the Manager can be configured (to setup an Adapter and a Retrier for example) as it reads "May be overridden by a subclass if you want to control the session configuration".

I have successfully implemented my Adapter and Retrier with queue control, so these are not my issue.

What I actually need to understand is, if I subclass AlamofireRequestBuilder (where createSessionManager is) and override createSessionManager to set my Adapter and Retrier, all of my generated class will be calling AlamofireImplementations's AlamofireRequestBuilder, not mine I suppose.

Get my point?

Solved:

You gotta subclass RequestBuilderFactory and AlamofireRequestBuilder, in the latter you override createSessionManager customizing whatever settings you may find useful.

After that you set SwaggerClientAPI.requestBuilderFactory = YourCustomClass()

Example bellow:

import UIKit
import Alamofire

class DCCustomAlamofireRequestBuilderFactory: RequestBuilderFactory {

func getBuilder<T>() -> RequestBuilder<T>.Type {

    return DCCustomAlamofireRequestBuilder<T>.self
}

}

class DCCustomAlamofireRequestBuilder: AlamofireRequestBuilder {

override func createSessionManager() -> SessionManager {

    let configuration = URLSessionConfiguration.default
    configuration.httpAdditionalHeaders = buildHeaders()

    let handler = DCAuthenticationManager()

    let manager = Alamofire.SessionManager(configuration: configuration)
    manager.adapter = handler
    manager.retrier = handler

    return manager
}

}

### AppDelegate

SwaggerClientAPI.requestBuilderFactory = DCCustomAlamofireRequestBuilderFactory()

If I try to use a custom requestBuilderFactory... my client crashes in.. I don't understand because I am only subclassing and not even doing anything with the manager yet:

AlamofireRequestBuilder.processRequest.:
Could not cast value of type 'Foundation._NSSwiftData' (0x1128e4438) to 'XXXSearch.SearchProfilesResponse' (0x11079f938)
import Foundation
import Alamofire
import XXXSearch

class XXXAlamofireRequestBuilderFactory: RequestBuilderFactory {
    func getNonDecodableBuilder<T>() -> RequestBuilder<T>.Type {
        return XXXAlamofireRequestBuilder<T>.self
    }

    func getBuilder<T:Decodable>() -> RequestBuilder<T>.Type {
        return XXXAlamofireDecodableRequestBuilder<T>.self
    }
}

open class XXXAlamofireRequestBuilder<T>: AlamofireRequestBuilder<T> {
    /**
     May be overridden by a subclass if you want to control the session
     configuration.
     */
    open override func createSessionManager() -> Alamofire.SessionManager {
        let configuration = URLSessionConfiguration.default
        configuration.httpAdditionalHeaders = buildHeaders()
        return Alamofire.SessionManager(configuration: configuration)
    }

}

open class XXXAlamofireDecodableRequestBuilder<T:Decodable>: XXXAlamofireRequestBuilder<T> {
}

in AppDelegate

        XXXSearchAPI.requestBuilderFactory = XXXAlamofireRequestBuilderFactory()

@sadanro100 any ideas?

looks like ur mixing up decodable and codable request builders

import UIKit
import Alamofire

class MACustomAlamofireRequestBuilderFactory: RequestBuilderFactory {

    func getNonDecodableBuilder<T>() -> RequestBuilder<T>.Type {
        return MACustomAlamofireRequestBuilder<T>.self
    }

    func getBuilder<T:Decodable>() -> RequestBuilder<T>.Type {
        return MACustomAlamofireDecodableRequestBuilder<T>.self
    }
}

class MACustomAlamofireRequestBuilder<T>: AlamofireRequestBuilder<T>   {

    open override func createSessionManager() -> Alamofire.SessionManager {

        let configuration = URLSessionConfiguration.default
        configuration.httpAdditionalHeaders = buildHeaders()

        let handler = MAAuthenticationManager()

        let manager = Alamofire.SessionManager(configuration: configuration)
        manager.adapter = handler
        manager.retrier = handler

        return manager
    }

}

class MACustomAlamofireDecodableRequestBuilder<T:Decodable>: AlamofireDecodableRequestBuilder<T>   {

    override func createSessionManager() -> Alamofire.SessionManager {

        let configuration = URLSessionConfiguration.default
        configuration.httpAdditionalHeaders = buildHeaders()

        let handler = MAAuthenticationManager()

        let manager = Alamofire.SessionManager(configuration: configuration)
        manager.adapter = handler
        manager.retrier = handler

        return manager
    }
}

Then put this somewhere:

SwaggerClientAPI.requestBuilderFactory = MACustomAlamofireRequestBuilderFactory()

let handler = MAAuthenticationManager()
we did't find AuthenticationManager() in swagger code gen.
shall we create new AuthenticationManager() , can you provide the code for AuthenticationManager().

Obviously MAAuthenticationManager is my authentication manager, you have to implement your own, thats where the retrier and adapter implementions go

this documentation gives examples on how to create Authentication Manager, Retrier, Adaptor etc..
https://github.com/Alamofire/Alamofire/blob/master/Documentation/AdvancedUsage.md

Was this page helpful?
0 / 5 - 0 ratings