Home > other >  Swift/Alamofire 5 ResponseSerializer generic stuct decode
Swift/Alamofire 5 ResponseSerializer generic stuct decode

Time:12-21

I'm trying to serialize some JSON data that is coming from an API endpoint, and because all of the endpoints would have the same structure I thought it would be best for me to implement something that doesn't need to get repeated again in code.

JSON response 1:

{
   "code":1100,
   "message":"Successfully created application",
   "data":{
      "key":116541
   }
}

JSON response 2:

{
   "code":1101,
   "message":"Successfully retrived",
   "data":{
      "id":116541,
      "name":"hallow"
   }
}

only data changes depending on the API endpoint. They would always come in this structure tho.

struct RawResponse<T: Decodable>: Decodable {
    let code: Int
    let message: String
    let data: T
}

This is where I don't know how to decode RawResponse using DecodableResponseSerializer from Alamofire.

final class CustomDecodeableResponseSerializer<T: Decodable>: ResponseSerializer {
   //...decoder setup

   //don't work, missing "T" because it's defined in the struct don't know where to add this 
   private lazy var successSerializer = DecodableResponseSerializer<RawResponse>(decoder: decoder)

   //This works as it's just pure struct without generic
   private lazy var errorSerializer = DecodableResponseSerializer<APIError>(decoder: decoder)

   //...public func serialize stuff
}

This would not work, because it's asking for that "T" defined in the struct, and "T" is passed in through an extension to DataRequest

@discardableResult
    func responseCustom<T: Decodable>(queue: DispatchQueue = DispatchQueue.global(qos: .userInitiated), of t: T.Type, completionHandler: @escaping (Result<T, APIError>) -> Void) -> Self {
        return response(queue: .main, responseSerializer: CustomDecodeableResponseSerializer<T>()) { response in
            switch response.result {
            case .success(let result):
                completionHandler(result)
            case .failure(let error):
                completionHandler(.failure(APIError(code: -1, message: error.localizedDescription)))
            }
        }
    }

So it can be called like this: User is another struct for JSON "data" field to be decoded as

session.request()
.validate()
.responseCustom(of: User.self){(response) in 
   //do stuff
}

I hope this makes sense...I know that if I just pass T instead of making a raw response struct, I can just repeat code and message in every response struct, and it will work. But I'm seeking to not repeat code and message throughout every response struct. Or is there is a simpler way to achieve this?

CodePudding user response:

You don't have it in your code, but make sure your CustomDecodeableResponseSerializer is returning RawResponse<T> from its serialize method. Then you can make sure your decoder for the success case is `DecodableResponseSerializer<RawResponse>(decoder: decoder).

Also, there's no reason to use DispatchQueue.global(). That parameter just controls where the completion handler is called, the actual serialization work is always performed in the background.

  • Related