Create a generic Data initializer for Decodable types using Swift

992 Views Asked by At
@objcMembers
public class MyResponse: NSObject, Codable {
    public let id: String?
    public let context: String?
    public let results: [MyResult]?
}

What is the proper way to parse MyResponse from Data in class extension?

I tried the following, but got error "Cannot assign to value: 'self' is immutable. Cannot assign value of type 'MyResponse' to type 'Self'."

extension MyResponse {
    public convenience init(data: Data) throws {
        self = try JSONDecoder().decode(MyResponse.self, from: data)
    }
}
2

There are 2 best solutions below

0
Denis Kozhukhov On BEST ANSWER

You can't overwrite class itself, but you can init it, init object from json and then assign values/ If take your code - it'll be something like this:

public class MyResponse: Codable {
    public var id: String?
    public var context: String?
    public var results: [MyResult]?
}

public extension MyResponse {
    convenience init(data: Data) throws {
        self.init()
        let object = try JSONDecoder().decode(MyResponse.self, from: data)
        self.id = object.id
        self.context = object.context
        self.results = object.results
    }
}

If you really don't need a class it's better to use struct instead of it, and it can be like this:

public struct MyResponse: Codable {
    public let id: String?
    public let context: String?
    public let results: [String]?
}

public extension MyResponse {
    init(data: Data) throws {
        self = try JSONDecoder().decode(MyResponse.self, from: data)
    }
}
1
Leo Dabus On

You can extend Decodable protocol and create a generic initializer:

extension Decodable {
    public init(data: Data, using decoder: JSONDecoder = JSONDecoder()) throws {
        self = try decoder.decode(Self.self, from: data)
    }
}