Search code examples
swiftalamofire

How can I pass nil to generic type parameter on Swift?


I'm using Alamofire5 and Swift 5.7.x.

There is a problem when I receive an response data from API server.

The response data default is:

{
    statusCode: 200
    message: "OK"
    items: null
}

But the "items" attribute can be anything data type. null, string, int, array, object..

I was about to solve using Generics. But I don't know how to handle the null.

It's API common code:

struct Response<T: Codable>: Codable {
    var statusCode: Int
    var message: String
    var items: T?
}

class API {
    func request<Parameters: Encodable, T: Decodable>(_ path: String,
                                                      method: HTTPMethod = .get,
                                                      params: Parameters? = nil,
                                                      completion: @escaping (Result<Response<T?>, NetworkError>) -> Void) {

        //some codes..
        
        AF.request("\(path)",
                   method: method,
                   parameters: params,
                   encoder: JSONParameterEncoder.prettyPrinted
        )
        .validate(statusCode: 200..<400)
        .validate(contentType: ["application/json"])
        .responseData { response in
            switch response.result {
            case .success(let data):
                guard let decodedData = try? JSONDecoder().decode(Response<T?>.self, from: data) else { return }
                print(decodedData)

                completion(.success(decodedData as Response<T?>))
            case.failure(let error):
                // some codes..
            }
        }

    }
}

It's caller:

        API.shared.request("/users/device", method: .post, params: reqParam) { (response: Result<Response<This is the problem..!!>, NetworkError>) in
            switch response {
            case .success(let data):
                print("userDevice updated")
                debugPrint(data)
            case .failure(let error):
                // some codes..
            }
        }

How can I pass nil on the caller?


Solution

  • Considering you have following response for all your API requests

    {
        statusCode: 200
        message: "OK"
        items: null
    }
    

    And your Codable struct as following

    struct Response<T: Codable>: Codable {
        var statusCode: Int
        var message: String
        var items: T? // Notice this is declared as Optional
    }
    

    As your items variable is declared as optional it will work as expected with little change.

    You can declare an Empty Struct which can be used when you are expecting null value for items key, like following.

    struct EmptyResponse: Codable {
    
    }
    

    You can use EmptyResponse struct as following:

    API.shared.request("/users/device", method: .post, params: reqParam) { (response: Result<Response<EmptyResponse>, NetworkError>) in
                switch response {
                case .success(let data):
                    debugPrint(data)
                    if let items = data.items {
                        // Use items here
                    } else {
                        // Items is nil
                    }
    
                case .failure(let error):
                    // some codes..
                }
            }