Swift — инициализировать объект модели с помощью init (из декодера :)

avatar
aios
1 июля 2021 в 16:19
250
2
0

Ниже представлена ​​структура моей модели

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"
        }
    }
}

Теперь в ViewModel я создаю экземпляр этой модели, используя приведенный ниже код

@Published var movieObj = MovieResponse()

Но возникает ошибка компиляции, говорящая, вызовите метод init(from decoder). Как правильно создать экземпляр модели в этом случае?

Источник
vadian
2 июля 2021 в 19:38
0

Несвязанное, но принудительное развертывание значения, декодированного с помощью decodeIfPresent, бессмысленно. Удалите IfPresent и восклицательный знак в конце. Вместо того, чтобы вызвать сбой, строка выдаст ошибку, если что-то пойдет не так.

Ответы (2)

avatar
Witek Bobrowski
1 июля 2021 в 16:38
2

Как гласит Руководство по языку Swift:

Swift предоставляет инициализатор по умолчанию для любой структуры или класса, который предоставляет значения по умолчанию для всех своих свойств и не предоставляет хотя бы один инициализатор сам.

Часть "и не предоставляет хотя бы один сам инициализатор"<80333370093456> здесь имеет решающее значение. Поскольку вы объявляете дополнительный инициализатор, вы должны либо объявить свой собственный инициализатор следующим образом:

init(
    totalResults: Int,
    response: String,
    error: String,
    movies: [Movie]
) {
    self.totalResults = totalResults
    self.response = response
    self.error = error
    self.movies = movies
}

или переместите соответствие Codable в расширение, чтобы Swift мог предоставить вам инициализатор по умолчанию. Это был бы предпочтительный способ сделать это (мое личное мнение, мне нравится переносить дополнительные соответствия протокола в расширения).

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)!
    }
}
aios
1 июля 2021 в 17:02
0

Первое решение хорошее и рабочее. Но когда я использую второй, это все та же ошибка.

avatar
SeaSpell
1 июля 2021 в 16:38
1

Вам нужно добавить еще один инициализатор, если вы не хотите использовать декодер. Swift предоставит вам его бесплатно тогда и только тогда, когда вы не напишете свой собственный инициализатор. Теперь, когда у вас есть один, вы теряете бесплатный.

добавить еще:

init() {
    //Your initializer code here
}

Если вы пытаетесь использовать инициализацию декодера, вам нужно использовать декодер для его вызова. Например, если это Json

@Published var movieObj = try? JSONDecoder().decode(MovieResponse.self, from: <#T##Data#>)