JSONDecoder corruption

I am having a problem decoding some JSON received via an API. One of the fields is an optional and it is this that seems to cause the fault. The first parsed notification is always corrupt unless read_at is null.


struct NotificationHead: Decodable, Identifiable {
var id: Int
var read_at: String?
var subject: String

init(id: Int,
     read_at: String?,
     subject: String) {
    self.id = id
    self.subject = subject
    self.read_at = read_at

static func parse(from json: String?) throws -> [NotificationHead] {
    let mess = "[{\"id\":646,\"read_at\":null,\"subject\":\"Migration \"}, {\"id\":693,\"read_at\":\"2023-04-09T16:27:11.220Z\",\"subject\":\"test\"}, {\"id\":688,\"read_at\":\"2023-04-07T17:06:10.172Z\",\"subject\":\"test message rgerg\"},{\"id\":646,\"read_at\":null,\"subject\":\"Migration \"}]"
    let notifications:[NotificationHead] = try JSONDecoder().decode([NotificationHead].self, from:mess.data(using: .utf8)!)
    return notifications


In this case the notifications result is fine. But if the first item in the JSON has a populated read_at field, then the first parsed notification has 'some' as the read_at value, not the date_time string, and subsequent use of the notification causes a memory failure

So trying to parse

let mess = "[{"id":693,"read_at":"2023-04-09T16:27:11.220Z","subject":"test"}, {"id":688,"read_at":"2023-04-07T17:06:10.172Z","subject":"test message rgerg"},{"id":646,"read_at":null,"subject":"Migration "}]"

Causes the problem. I can swap the items around and as long as the first notification has a read_at value, then the first parsed notification ALWAYS has the read_at as 'some', and all subsequent notifications are parsed correctly.

Any ideas please? I'm new to Swift and this has me flumuxed!

I think what's happening is different from what you think. If decoding failed, then the decoder would throw an error that you could print to find out what went wrong.

Rather, it looks like decoding is succeeding, but your "read_at" property is an optional. Depending where and how you're looking at its value, it may display as "some" because .some(value) is the actual implementation of the non-nil case of Optional (which is implemented in Swift as an enum type with cases .none and .some).

I suspect everything has worked just fine, but you'll need to unwrap the optional string to get the string's value.

As written there's no issue in the fragment you provided, the issue is elsewhere.
I'd recommend you to make a small reproducible test app that shows the crash – in the process of making it you'll see the issue yourself (and if not just post that sample app here).

A couple of unrelated suggestions:

  • you can remove the srtuct's "init" as it's not any different from the built-in member-wise synthesised initialiser.
  • with convertFromSnakeCase keyDecodingStrategy you'd be able naming the field "readAt".
  • Unfortunately iso8601 dateDecodingStrategy doesn't support fractional seconds, otherwise you could've have readAt variable typed as Date?. You could use a custom dateDecodingStrategy.