Create a generic Data initializer for Decodable types using Swift
Asked Answered
R

2

3
@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)
    }
}
Retroactive answered 7/1, 2021 at 15:37 Comment(0)
C
0

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)
    }
}
Compulsion answered 7/1, 2021 at 16:8 Comment(0)
T
3

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)
    }
}
Twice answered 7/1, 2021 at 17:33 Comment(1)
If you are looking for a solution like this, but do not want to give every Decodable type a new initializer, you can create a dummy protocol to which only your type conforms to and then extend that dummy protocol. This is the solution used internally in the Swift standard library. For more details, see my answer to a related question.Laufer
C
0

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)
    }
}
Compulsion answered 7/1, 2021 at 16:8 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.