magnuskahr

writing code

Decode arrays where items can fail

Lets say we have a structure:

struct Participant: Codable {
    let name: String
    let startingTime: Date
}

and that we will fetch these participants from an endpoint, providing the following json:

[{
    "name": "John",
    "startingTime": 581167640.06502903
}, {
    "name": "Mark",
    "startingTime": 582031640.06502903
}, {
    "name": "Tobias"
}]

We see that the third object in the JSON data, doesn't conform to Participant since it does not have any valid startingTime, so the question is now, how we will fetch all the valid participants?

Creating a failable wrapper

What we will have to do, is to make a failable wrapper, from where we can extract the participants if it was successfully created; and now we are at it, we will make it generic so it works for all kinds of Decodables.

struct FailableDecodable<T: Decodable>: Decodable {
    let value: T?
    init(from decoder: Decoder) throws {
		let container = try decoder.singleValueContainer()
		self.value = try? container.decode(T.self)
	}
}

The magic happens in the custom decoding initializer, where we try to decode something of the type T and saves it into value. If this fails, the value will be nil, why we on a sequence of FailableDecodable can use .compactMap to strip out all nil's and map over the value; why we now safely can decode all the objects in the json that conforms to our model:

let decoder = JSONDecoder()
let wrapped = try! decoder.decode([FailableDecodable<Participant>].self, from: data)
let participants = wrapped.compactMap { $0.value }

To see a fully working example, see this gist