Swift 4 JSON decoding when a type is known only at runtime

Is it possible to use the Decodable protocol in Swift 4 to decode a JSON object when the type to be decoded is known only at runtime?

I have a species registry that maps the String identifier to the type we want to decode, as shown below:

 import Foundation struct Person: Decodable { let forename: String let surname: String } struct Company: Decodable { let officeCount: Int let people: [Person] } let registry: [String:Decodable.Type] = [ "Person": Person.self, "Company": Company.self ] let exampleJSON = """ { "forename": "Bob", "surname": "Jones" } """.data(using: .utf8)! let t = registry["Person"]! try! JSONDecoder().decode(t, from: exampleJSON) // doesn't work :-( 

Am I on the right lines here or is there a better way?

+5
source share
2 answers

Your design is really unique, but, unfortunately, I think that you find yourself in an extreme case of the Swift system. In principle, the protocol does not correspond to itself and, as such, your general Decodable.Type is not enough here (i.e. you really need a specific type to satisfy the system requirements of the type). This may explain your error:

Cannot call decode using argument list of type (Decodable.Type, from: Data) . The expected list of type arguments (T.Type, from: Data) .

But, having said that, there really is (dirty!) To crack it. First, create a DecodableWrapper dummy to save your runtime-ish Decodable :

 struct DecodableWrapper: Decodable { static var baseType: Decodable.Type! var base: Decodable init(from decoder: Decoder) throws { self.base = try DecodableWrapper.baseType.init(from: decoder) } } 

then use it as follows:

 DecodableWrapper.baseType = registry["Person"]! let person = try! JSONDecoder().decode(DecodableWrapper.self, from: exampleJSON).base print("person: \(person)") 

displays the expected result:

person: Person (first name: "Bob", last name: "Jones")

+7
source

Paulo's workaround has the disadvantage that it is not thread safe. Here is an example of a simpler solution that allows you to decode a value without having a specific type:

 struct DecodingHelper: Decodable { private let decoder: Decoder init(from decoder: Decoder) throws { self.decoder = decoder } func decode(to type: Decodable.Type) throws -> Decodable { let decodable = try type.init(from: decoder) return decodable } } func decodeFrom(_ data: Data, to type: Decodable.Type) throws -> Decodable { let decodingHelper = try JSONDecoder().decode(DecodingHelper.self, from: data) let decodable = try decodingHelper.decode(to: type) return decodable } 
+3
source

Source: https://habr.com/ru/post/1272378/


All Articles