Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

When decoding a Codable struct from JSON, how do you initialize a property not present in the JSON?

Tags:

swift

codable

Say that in this example here, this struct

struct Reminder: Identifiable {
    var id: String = UUID().uuidString
    var title: String
    var dueDate: Date
    var notes: String? = nil
    var isComplete: Bool = false
}

is instead decoded from JSON array values (rather than constructed like in the linked example). If each JSON value were to be missing an "id", how would id then be initialized? When trying this myself I got an error keyNotFound(CodingKeys(stringValue: "id", intValue: nil), Swift.DecodingError.Context(codingPath: [_JSONKey(stringValue: "Index 0", intValue: 0)], debugDescription: "No value associated with key CodingKeys(stringValue: \"id\", intValue: nil) (\"id\").", underlyingError: nil)).

like image 797
Austin Conlon Avatar asked Dec 05 '25 17:12

Austin Conlon


2 Answers

You can simply use coding keys, like this:

struct Reminder: Identifiable, Codable {
    var id: String = UUID().uuidString
    var title: String
    var dueDate: Date
    var notes: String? = nil
    var isComplete: Bool = false
    
    enum CodingKeys: String, CodingKey {
        case title
        case dueDate
        case notes
        case isComplete
    }
}

That way, the only properties that will be encoded/decoded are the ones that have a corresponding coding key.

like image 131
Austin Avatar answered Dec 11 '25 08:12

Austin


Previous answer is nice.

To have full control, you need to override the default decoding.

struct Reminder: Identifiable {
    var id: String = UUID().uuidString
    var title: String
    var dueDate: Date
    var notes: String? = nil
    var isComplete: Bool = false
    
    init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)
    
        id = (try? values.decode(String.self, forKey: .id)) ?? UUID()
        title = try values.decode(String.self, forKey: . title)
        dueDate = try values.decode(Date.self, forKey: . dueDate)
        notes = try? values.decode(String.self, forKey: .notes)
        isComplete = try values.decode(Bool.self, forKey: . isComplete)
    }
}

I go a little bit beyond the question, but why is id not set in the JSON?

  • If id is a database key, then it must be present in the json.
  • On the other side, if id is a unique identifier to identify items in UI, then it is much safer and clean to use a dedicated object that describes the object representation.
struct ReminderItem: Identifiable {
   let id = UUID()
   let reminder: Reminder
}

By the way, it works the same way for encoding.

https://developer.apple.com/documentation/foundation/archives_and_serialization/encoding_and_decoding_custom_types

like image 36
Moose Avatar answered Dec 11 '25 08:12

Moose



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!