SchmidtFx
SchmidtFx

Reputation: 626

How to encode protocol property default implementation to dictionary

I want to make a dictionary from a encodable struct with a default implementation property.

struct MyStruct: MyStructProtocol {
    var value: String
}

The struct implements a protocol. That protocol has two variables. One variable has a default implementation.

protocol MyStructProtocol: Encodable {
    var defaultValue: String { get }
    var value: String { set get }
}

extension MyStructProtocol {
    var defaultValue: String { return "my-default-value" }
}

To that I use that Encodable Extension from How can I use Swift’s Codable to encode into a dictionary?:

extension Encodable {
    var asDictionary: [String: Any]? {
        guard let data = try? JSONEncoder().encode(self) else { return nil }
        return (try? JSONSerialization.jsonObject(with: data, options: .allowFragments)).flatMap { $0 as? [String: Any] }
    }
}

So when I instantiate the struct and "encode" it to a dictionary:

let myStruct = MyStruct(value: "my-value")
let myStructDictionary = myStruct.asDictionary

then the defaultValue is not included:

["value": "my-value"]

But what I need is (included the defaultValue):

["defaultValue": "my-default-value", "value": "my-value"]

Upvotes: 0

Views: 655

Answers (3)

Ahmad F
Ahmad F

Reputation: 31695

That's because the default value for defaultValue has been implemented in extension for the protocol, which means that it is a computed property.

struct MyStruct: MyStructProtocol {
    var value: String

    enum CodingKeys: String, CodingKey {
        case value
        case defaultValue = "my-default-value"
    }

    func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(value, forKey: .value)
        try container.encode(defaultValue, forKey: .defaultValue)
    }
}

Upvotes: 0

vadian
vadian

Reputation: 285290

The synthesized encoder considers only the members in the struct, not any properties in a protocol extension nor computed properties.

You have to write a custom initializer. And I'd prefer to make the struct adopt Encodable rather than the protocol.

struct MyStruct: MyStructProtocol, Encodable {
    var value: String

    private enum CodingKeys: String, CodingKey { case value, defaultValue }

    func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(value, forKey: .value)
        try container.encode(defaultValue, forKey: .defaultValue)
    }
}

protocol MyStructProtocol { ...

Upvotes: 3

Witek Bobrowski
Witek Bobrowski

Reputation: 4269

Encodable will not recognise computed properties. To fix that, override encode(to:) function as shown in the official documentation https://developer.apple.com/documentation/foundation/archives_and_serialization/encoding_and_decoding_custom_types

EDIT: A possible solution to the problem: How to use computed property in a codable struct (swift)

Upvotes: 0

Related Questions