Below is my model struct
struct MovieResponse: Codable {
    
    var totalResults: Int
    var response: String
    var error: String
    var movies: [Movie]
    
    enum ConfigKeys: String, CodingKey {
        case totalResults
        case response = "Response"
        case error = "Error"
        case movies
    }
    
    init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)
        self.totalResults = try values.decodeIfPresent(Int.self, forKey: .totalResults)!
        self.response = try values.decodeIfPresent(String.self, forKey: .response)!
        self.error = try values.decodeIfPresent(String.self, forKey: .error) ?? ""
        self.movies = try values.decodeIfPresent([Movie].self, forKey: .movies)!
    }
}
extension MovieResponse {
    struct Movie: Codable, Identifiable {
        var id = UUID()
        var title: String
        var year: Int8
        var imdbID: String
        var type: String
        var poster: URL
        
        enum EncodingKeys: String, CodingKey {
            case title = "Title"
            case year = "Year"
            case imdmID
            case type = "Type"
            case poster = "Poster"
        }
    }
}
Now in a ViewModel, I am creating an instance of this model using the below code
@Published var movieObj = MovieResponse()
But there is a compile error saying, call init(from decoder) method. What is the proper way to create a model instance in this case?
As the Swift Language Guide reads:
Swift provides a default initializer for any structure or class that provides default values for all of its properties and doesn’t provide at least one initializer itself.
The "and doesn’t provide at least one initializer itself" part is crucial here. Since you are declaring an additional initializer you should either declare your own initialized like so:
init(
    totalResults: Int,
    response: String,
    error: String,
    movies: [Movie]
) {
    self.totalResults = totalResults
    self.response = response
    self.error = error
    self.movies = movies
}
or move Codable conformance to an extension so Swift can provide you with a default initialiser. This would be a preferred way to do it (my personal opinion, I like to move additional protocol conformances to extensions).
struct MovieResponse {
    var totalResults: Int
    var response: String
    var error: String
    var movies: [Movie]
}
extension MovieResponse: Codable {
    enum ConfigKeys: String, CodingKey {
        case totalResults
        case response = "Response"
        case error = "Error"
        case movies
    }
    init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)
        self.totalResults = try values.decodeIfPresent(Int.self, forKey: .totalResults)!
        self.response = try values.decodeIfPresent(String.self, forKey: .response)!
        self.error = try values.decodeIfPresent(String.self, forKey: .error) ?? ""
        self.movies = try values.decodeIfPresent([Movie].self, forKey: .movies)!
    }
}
                        If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With