Search code examples
swiftprotocolscodableswift4.2

Why does my special Codable protocol work differently than Swift's Codable with Array?


With Codable, I am able to create the following extension

extension Decodable {
    public static func decode(data: Data, decoder: JSONDecoder = .default) -> Self? {
        do {
            return try decoder.decode(self, from: data)
        } catch let error as NSError {
            CodableKit.log(message: "\(error.userInfo)")
            return nil
        }
    }
}

and use it on both single objects and array types, for example

let person = Person.decode(data: personData)   // single
let people = [Person].decode(data: peopleData) // array 

The 2 lines above compile without issues.

Now, I want to create a new protocol similar to Codable

public typealias JsonCodable = JsonDecodable & JsonEncodable

public protocol JsonDecodable: Decodable {
    static func decode(data: Data?, decoder: JSONDecoder) -> Self?
}

extension JsonDecodable {
    static func decode(data: Data?, decoder: JSONDecoder) -> Self? {
        ....
    }
}

When I use try to use JsonDecodable the same way I do with Codable, i get the following compiler error

Type '[Person]' has no member 'decode';

let person = Person.decode(data: personData)   // this works
let people = [Person].decode(data: peopleData) // this does not

How can I get JsonDecodable to decode into an array of a model the same way I can when extending Codable?


Solution

  • The error message might be more useful if it used the unsugared typename:

    Type 'Array<Person>' has no member 'decode';

    Person may conform to your protocol, but Array does not. Swift explicitly declares that Arrays are Decodable if their elements are. You just need to do the same:

    extension Array : JsonDecodable where Element : JsonDecodable {
        static func decode(data: Data?, decoder: JSONDecoder) -> Self? {
            // Decode each element and return an array
        }
    }
    

    This uses a feature called "Conditional Conformance", which allows containers generally to conform to a protocol if the type they hold also does.