Swift - 使用 init 初始化模型对象(来自解码器:)

Posted

技术标签:

【中文标题】Swift - 使用 init 初始化模型对象(来自解码器:)【英文标题】:Swift - Initialise model object with init(from decoder:) 【发布时间】:2021-07-01 16:19:22 【问题描述】:

下面是我的模型结构

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)方法。在这种情况下创建模型实例的正确方法是什么?

【问题讨论】:

不相关但强制解开由decodeIfPresent 解码的值是没有意义的。删除IfPresent 和结尾的感叹号。如果出现问题,该行将引发错误,而不是引发崩溃。 【参考方案1】:

Swift Language Guide 写道:

Swift 为任何结构或类提供了一个默认初始化器,它为其所有属性提供默认值,但本身不提供至少一个初始化器。

“并且本身不提供至少一个初始化程序” 部分在这里至关重要。由于您要声明一个额外的初始化程序,您应该像这样声明您自己的初始化程序:

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)!
    

【讨论】:

第一个解决方案很好并且有效。但是当我使用第二个时,它仍然是同样的错误。【参考方案2】:

如果您不想使用解码器,则需要添加另一个初始化程序。当且仅当您不编写自己的初始化程序时,Swift 才会免费为您提供一个。既然你有了一个,你就失去了一个免费的。

添加另一个:

init() 
    //Your initializer code here

如果您尝试使用解码器 init,则需要使用解码器来调用它。例如,如果它是 Json

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

【讨论】:

以上是关于Swift - 使用 init 初始化模型对象(来自解码器:)的主要内容,如果未能解决你的问题,请参考以下文章

Swift 中 JSON 的模型类和核心数据的相同类

Swift基础-init详解

Swift 构造函数

swift kvc赋值

Swift 模型属性

Swift学习(3面向对象)