1

I'm decoding a simple structure and ran into unexpected behavior.

struct Contacts: Codable {
  struct Recipient: Codable {
    let name: String
  }

  let recipients: [Recipient]
}

do {
    let jsonData = SOME STING WITH VALID JSON
    let contacts = try JSONDecoder().decode(Contacts.self, from: jsonData)
}
catch {
}

This decodes just fine. If I do this simple change to the structure, it no longer decodes.

struct Contacts: Codable {
  struct Recipient: Codable {
    let name: String
  }

  let recipients: [Recipient] = []
}

Now JSONDecoder won't decode the exact same string. Why does the default initialization of the array cause the decoder to stop working?

5
  • 2
    Maybe because it is declared letand can’t be changed once it is initialized? Commented Jan 30, 2020 at 16:50
  • 1
    A let property can only be set once so if you're giving it a default value then the initializer generated by Codable can't set it again. If you change it to var it will decode. It will still fail to decode if the recipients key isn't present in the json though which is what I'm assuming you were trying to prevent Commented Jan 30, 2020 at 16:53
  • Added a custom decoder init and do decodeIfPresent([Recipient].self forKey: .recipient) ?? [] to default initialize it.. Commented Jan 30, 2020 at 16:58
  • Why do you want to do that? Let the decoder initialize the stuff unless you implement a custom init(from decoder with custom behavior. If instances of the struct are created without the decoder add an init method providing a default value. Commented Jan 30, 2020 at 17:12
  • Yep, 'let' was the issue--brain freeze on my part. Commented Jan 30, 2020 at 19:35

2 Answers 2

3

Change your code to read:

struct Contacts: Codable {
  struct Recipient: Codable {
    let name: String
  }

  var recipients: [Recipient] = []
}

The issue (as described in the comments above) is that your initial declaration of the variable is immutable (meaning it cannot be changed after it is initialized). So, if you declare the initial value for a let as [] then you cannot subsequently change it. By changing let to var you are declaring the variable as mutable (meaning it can be changed) which allows you to both supply an initial value as well as change the value later.

Sign up to request clarification or add additional context in comments.

Comments

2

Your let here isn't a "default initialization." It's defining recipients as a constant with a specific, compile-time, value. If you want a default value, then you can create a initializer for that if you like:

struct Contacts: Codable {
  struct Recipient: Codable {
    let name: String
  }

  let recipients: [Recipient]

  init(recipients: [Recipient] = []) { self.recipients = recipients }
}

Generally speaking on this kind of very simple data type, I'd make recipients a var instead (see John Ayers's answer for example). It's much more flexible, while maintaining all the advantages of a value type. But in cases where you want a let constant with a default (rather than a single value), you need an init.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.