Alamofire 5 Any In Dictionary

Viewed 2188

I am trying to make a post request with Alamofire 5. I have to use Dictionary<String, Any> for parameters. Because I am writing a wrapper for Alamofire. But it seems i can't be able to use Any object in a dictionary because Alamofire gives me a compiler error:

Value of protocol type 'Any' cannot conform to 'Encodable'; only struct/enum/class types can conform to protocols

What i've tried:

    let encodedParameters = Dictionary<String, Any>

    AF.request(url, method: .get, parameters: encodedParameters, headers: headers)

Some values will be string others will be integer in my dictionary. So i can't use constant type. How can i resolve this problem?

3 Answers

It's because you are using the newer method which requires the parameters parameter to be Encodable.Use the older Alamofire method and you will be fine:

AF.request(url, method: .get, parameters: encodedParameters, encoding: JSONEncoding.default, headers: headers)

Update: If you want to use the latest Alamofire 5 syntax create a struct and confirm it to encodable. Then create an object of the same struct with values and pass it.

To use the new request, you can create your own structs for your request parameters:

// you might have...
struct FooRequestParameters : Codable {
    let paramName1: Int
    let paramName2: String
}

// for another type of request, you might have different parameters...
struct BarRequestParameters: Codable {
    let somethingElse: Bool
}

And you can pass a FooRequestParameters(paramName1: 1, paramName1: "hello") instead of your dictionary. This would be the same as passing the dictionary:

[
    "paramName1": 1,
    "paramName2": "hello"
]

The rationale behind this API change is likely to have more safety. With a [String: Any], you could very easily, say, give a String value to a parameter that is supposed to be Int, or type a parameter's name wrongly, or missed some parameters without realising... etc etc.

If you want pass parameters of [String: Any] you don't need to create structs and adopt the encodable protocol. This can be achieved like this:

import Foundation
import Alamofire

enum ServiceRouter {
    case fetchCountries
    case fetchUserRepositories
    
    var baseURL: String {
        switch self {
        case .fetchCountries:
            return "https://restcountries.eu/rest/v2"
        }
    }
    
    var path: String {
        switch self {
        case .fetchCountries:
            return "/all"
        }
    }
    
    var method: HTTPMethod {
        switch self {
        case .fetchCountries:
            return .get
        default:
            return .post
        }
    }
    
    var parameters: Parameters? {
        switch self {
        case .fetchUserRepositories:
            return ["per_page": 100]
            
        default:
            return [:]
        }
    }
}

// MARK: - URLRequestConvertible
extension ServiceRouter: URLRequestConvertible {
    func asURLRequest() throws -> URLRequest {
        let url = try baseURL.asURL().appendingPathComponent(path)
        var request = URLRequest(url: url)
        request.method = method
        if method == .get {
            request = try URLEncoding.default.encode(request, with: parameters)
        } else if method == .post {
            request = try JSONEncoding.default.encode(request, with: parameters)
        }
        return request
    }
}

And you can use this request as

AF.request(ServiceRouter.fetchCountries).responseData { (response) in 
Related