鸡肉味嘎嘣脆
鸡肉味嘎嘣脆

Reputation: 233

How to encode Dictionary with JSONEncoder in Swift 4

I want to encode Dictionary to json with JSONEncoder. It seems like a Request, receive a dictionary as parameter and encode it to json as http body. The code is looks like this:

let dict = ["name": "abcde"]

protocol Request {
    var params: [String: Encodable] { get set }
    func encode<T>(_ value: T) throws -> Data where T : Encodable
}

extension Request {
    func encode<T>(_ value: T) throws -> Data where T : Encodable {
        return try JSONEncoder().encode(value)
    }

    var body: Data? {
        if let encoded = try? self.encode(self.params) {
            return encoded
        }
        return nil
    }
}

struct BaseRequest: Request {
    var params: [String : Encodable]
}

let req = BaseRequest(params: dict)
let body = req.body

But this code occurs error

Fatal error: Dictionary<String, Encodable> does not conform to Encodable because Encodable does not conform to itself. You must use a concrete type to encode or decode.

How could I make this encodable?

Upvotes: 6

Views: 13692

Answers (2)

Guy Kogus
Guy Kogus

Reputation: 7341

If you want to define your struct as conforming to Codable, you can do it like this:

struct Model: Codable {
    var param1: String
    var param2: Int
}

let model = Model(param1: "test", param2: 0)
let encoded = try? JSONEncoder().encode(model)
let decoded = try? JSONDecoder().decode(Model.self, from: encoded!)

It won't really work if you set params: [String: Any] because the encoders/decoders don't know how to encode/decode Any, but they can do it for the primitive types.

If you want more help, you should read more about the new Codable protocol. I recommend this: https://hackernoon.com/everything-about-codable-in-swift-4-97d0e18a2999

Upvotes: -1

Timofey Solonin
Timofey Solonin

Reputation: 1403

You have to introduce type erasure as follows:

struct AnyEncodable: Encodable {

    let value: Encodable
    init(value: Encodable) {
        self.value = value
    }

    func encode(to encoder: Encoder) throws {
        try value.encode(to: encoder)
    }

}

struct Model: Encodable {

    var params: [String: AnyEncodable]

}

let encoder = JSONEncoder()
encoder.outputFormatting = .prettyPrinted
let json = try! encoder.encode(
    Model(
        params: [
            "hello" : AnyEncodable.init(value: "world")
        ]
    ).params
)
print(String(data: json, encoding: .utf8))

Upvotes: 3

Related Questions