无法自动合成:SwiftData中@Model的Landmark类为何不兼容Codable?
解决SwiftData @Model类无法自动合成Codable的问题
问题描述
将苹果官方Landmarks应用迁移至SwiftData时,标注@Model的Landmark类始终无法符合Encodable/Decodable协议,编译报错:
Type 'Landmark' does not conform to protocol 'Decodable'
Type 'Landmark' does not conform to protocol 'Encodable'
展开@Model宏时提示:
Cannot automatically synthesize 'Decodable' because 'any SwiftData.BackingData' does not conform to 'Decodable'
对应代码:
import Foundation import SwiftUI import CoreLocation import SwiftData @Model final class Landmark: Hashable, Codable, Identifiable { var id: Int var name: String var park: String var state: String var dezcription: String var isFavorite: Bool var isFeatured: Bool var category: Category private var coordinates: Coordinates private var imageName: String init(id:Int = Int.random(in: 2000...Int.max), name:String = "", park: String = "", state:String = "", dezcription: String = "", isFavorite: Bool = false, isFeatured:Bool = false, category: Category = Category.mountains, coordinates: Coordinates = Coordinates(latitude: 0, longitude: 0), imageName:String = "umbagog") { self.id = id self.name = name self.park = park self.state = state self.dezcription = dezcription self.isFavorite = isFavorite self.isFeatured = isFeatured self.category = category self.coordinates = coordinates self.imageName = imageName } enum Category: String, CaseIterable, Codable { case lakes = "Lakes" case rivers = "Rivers" case mountains = "Mountains" } var image: Image { Image(imageName) } var locationCoordinate: CLLocationCoordinate2D { CLLocationCoordinate2D( latitude: coordinates.latitude, longitude: coordinates.longitude) } struct Coordinates: Hashable, Codable { var latitude: Double var longitude: Double } }
注:随机ID、默认坐标等设计问题并非本次核心。
已尝试但未解决的方法:
- 注释/隔离疑似有问题的类型
- 确保所有类型符合Codable协议
- 初始化器包含所有属性
- 查阅@Model类使用规范
- 为Image计算属性添加@Transient标记
- 将Coordinates和Category移至类外部
- 添加唯一UUID属性
解决方法
问题根源在于@Model宏会自动为类注入any SwiftData.BackingData类型的关联属性,该类型不遵循Codable,导致系统无法自动合成Codable实现。必须手动实现Encodable和Decodable方法,仅处理自定义的属性:
- 定义
CodingKeys枚举,指定需要编码/解码的属性 - 手动实现
encode(to:)方法,编码所有自定义存储属性 - 手动实现
required init(from:)方法,解码属性并创建实例
修改后的完整代码:
import Foundation import SwiftUI import CoreLocation import SwiftData @Model final class Landmark: Hashable, Codable, Identifiable { var id: Int var name: String var park: String var state: String var dezcription: String var isFavorite: Bool var isFeatured: Bool var category: Category private var coordinates: Coordinates private var imageName: String init(id:Int = Int.random(in: 2000...Int.max), name:String = "", park: String = "", state:String = "", dezcription: String = "", isFavorite: Bool = false, isFeatured:Bool = false, category: Category = Category.mountains, coordinates: Coordinates = Coordinates(latitude: 0, longitude: 0), imageName:String = "umbagog") { self.id = id self.name = name self.park = park self.state = state self.dezcription = dezcription self.isFavorite = isFavorite self.isFeatured = isFeatured self.category = category self.coordinates = coordinates self.imageName = imageName } // 手动实现Codable相关逻辑 enum CodingKeys: String, CodingKey { case id, name, park, state, dezcription, isFavorite, isFeatured, category, coordinates, imageName } func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(id, forKey: .id) try container.encode(name, forKey: .name) try container.encode(park, forKey: .park) try container.encode(state, forKey: .state) try container.encode(dezcription, forKey: .dezcription) try container.encode(isFavorite, forKey: .isFavorite) try container.encode(isFeatured, forKey: .isFeatured) try container.encode(category, forKey: .category) try container.encode(coordinates, forKey: .coordinates) try container.encode(imageName, forKey: .imageName) } required init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) id = try container.decode(Int.self, forKey: .id) name = try container.decode(String.self, forKey: .name) park = try container.decode(String.self, forKey: .park) state = try container.decode(String.self, forKey: .state) dezcription = try container.decode(String.self, forKey: .dezcription) isFavorite = try container.decode(Bool.self, forKey: .isFavorite) isFeatured = try container.decode(Bool.self, forKey: .isFeatured) category = try container.decode(Category.self, forKey: .category) coordinates = try container.decode(Coordinates.self, forKey: .coordinates) imageName = try container.decode(String.self, forKey: .imageName) } enum Category: String, CaseIterable, Codable { case lakes = "Lakes" case rivers = "Rivers" case mountains = "Mountains" } var image: Image { Image(imageName) } var locationCoordinate: CLLocationCoordinate2D { CLLocationCoordinate2D( latitude: coordinates.latitude, longitude: coordinates.longitude) } struct Coordinates: Hashable, Codable { var latitude: Double var longitude: Double } }
通过手动实现Codable逻辑,避开SwiftData注入的非Codable属性,即可让Landmark类同时满足SwiftData模型和Codable协议的要求。
内容的提问来源于stack exchange,提问作者Nerdy Bunz
相关产品推荐
相关产品推荐

