Zack Shapiro
Zack Shapiro

Reputation: 7008

Default enum value for optional key

I have a JSON object that always has text, and sometime has a format key and when it doesn't, I want to use the .regular case for my SubStep.Format enum.

I know I need to override the init in SubStep but how do I do it such that I can detect the format key, if it's nil, use .regular, and then just decode the text key that's always there?

Thanks

struct SubStep: Decodable {
    enum Format: String, Decodable {
        case bold = "bold"
        case regular = "regular"

        init(from decoder: Decoder) {
            do {
                let label = try decoder.singleValueContainer().decode(String.self)
                self = Format(rawValue: label) ?? .regular
            } catch {
                self = .regular
            }
        }
    }

    let format: SubStep.Format
    let text: String

    init(from decoder: Decoder) throws {
       // something in here
    }
}

Upvotes: 0

Views: 290

Answers (2)

Sulthan
Sulthan

Reputation: 130200

As an alternative solution, you don't have to insert the default value during parsing - you can use a computed property instead.

struct SubStep: Decodable {
    enum Format: String, Decodable {
        case bold
        case regular
    }

    private let formatOptional: Format?
    let text: String

    private enum CodingKeys: String, CodingKey {
        case formatOptional = "format"
        case text
    }

    var format: Format {
        return formatOptional ?? .regular
    }
}

Upvotes: 1

David Pasztor
David Pasztor

Reputation: 54805

You don't need to create a custom init(from:) method for Format, you only need one for SubStep. You need to use container.decodeIfPresent(_:forKey:) to decode a key which might not be present in your JSON, in which case it returns nil.

struct SubStep: Decodable {
    enum Format: String, Decodable {
        case bold
        case regular
    }

    let format: SubStep.Format
    let text: String

    private enum CodingKeys: String, CodingKey {
        case text, format
    }

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        self.text = try container.decode(String.self, forKey: .text)
        self.format = try container.decodeIfPresent(Format.self, forKey: .format) ?? .regular
    }
}

Unrelated to your issue, but you don't need to provide a String rawValue for your enum cases if their rawValue would exactly match the name of the case, the compiler will autosynthetise those for you.

Upvotes: 2

Related Questions