Swift数据模型:如何让reviewable字段兼容Movie/Show/Episode多类型?
Swift中Review模型多类型Reviewable字段的解码实现
方案一:枚举关联值(类型安全,推荐)
利用Swift枚举的关联值特性,结合自定义解码逻辑,根据reviewable_type字段匹配对应的模型类型。
1. 定义Reviewable枚举
enum Reviewable: Decodable { case movie(Movie) case show(Show) case episode(Episode) }
2. 改造Review模型实现自定义解码
struct Review: Identifiable, Decodable { var id: Int var reviewable_type: String var score: Int? var reviewable: Reviewable var user: User var created_at: String? var created: String? var text: String? private enum CodingKeys: String, CodingKey { case id, reviewable_type, score, reviewable, user, created_at, created, text } init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) // 解析固定字段 id = try container.decode(Int.self, forKey: .id) reviewable_type = try container.decode(String.self, forKey: .reviewable_type) score = try container.decodeIfPresent(Int.self, forKey: .score) user = try container.decode(User.self, forKey: .user) created_at = try container.decodeIfPresent(String.self, forKey: .created_at) created = try container.decodeIfPresent(String.self, forKey: .created) text = try container.decodeIfPresent(String.self, forKey: .text) // 根据reviewable_type解析对应模型 switch reviewable_type { case "Movie": let movie = try container.decode(Movie.self, forKey: .reviewable) reviewable = .movie(movie) case "Show": let show = try container.decode(Show.self, forKey: .reviewable) reviewable = .show(show) case "Episode": let episode = try container.decode(Episode.self, forKey: .reviewable) reviewable = .episode(episode) default: throw DecodingError.dataCorruptedError( forKey: .reviewable, in: container, debugDescription: "未知的reviewable类型: \(reviewable_type)" ) } } }
3. 使用时的类型判断
switch review.reviewable { case .movie(let movie): // 处理Movie类型逻辑,比如访问movie.title case .show(let show): // 处理Show类型逻辑,比如访问show.name case .episode(let episode): // 处理Episode类型逻辑,比如访问episode.episodeNumber }
方案二:协议+类型转换(适配已有协议场景)
如果已有Movie/Show/Episode模型,可通过定义统一协议并实现自定义解码来处理。
1. 定义公共协议
protocol Media: Decodable { // 定义公共属性,比如id、标题等 var id: Int { get } var title: String { get } } // 让已有模型遵守协议 extension Movie: Media {} extension Show: Media {} extension Episode: Media {}
2. 改造Review模型的解码逻辑
struct Review: Identifiable, Decodable { var id: Int var reviewable_type: String var score: Int? var reviewable: Media var user: User var created_at: String? var created: String? var text: String? private enum CodingKeys: String, CodingKey { case id, reviewable_type, score, reviewable, user, created_at, created, text } init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) // 解析固定字段 id = try container.decode(Int.self, forKey: .id) reviewable_type = try container.decode(String.self, forKey: .reviewable_type) score = try container.decodeIfPresent(Int.self, forKey: .score) user = try container.decode(User.self, forKey: .user) created_at = try container.decodeIfPresent(String.self, forKey: .created_at) created = try container.decodeIfPresent(String.self, forKey: .created) text = try container.decodeIfPresent(String.self, forKey: .text) // 根据类型解析对应Media实例 switch reviewable_type { case "Movie": reviewable = try container.decode(Movie.self, forKey: .reviewable) case "Show": reviewable = try container.decode(Show.self, forKey: .reviewable) case "Episode": reviewable = try container.decode(Episode.self, forKey: .reviewable) default: throw DecodingError.dataCorruptedError( forKey: .reviewable, in: container, debugDescription: "不支持的媒体类型" ) } } }
3. 使用时的类型转换
if let movie = review.reviewable as? Movie { // 处理Movie类型 } else if let show = review.reviewable as? Show { // 处理Show类型 } else if let episode = review.reviewable as? Episode { // 处理Episode类型 }
关键注意点
- 确保
reviewable_type的字符串值与代码中的case完全匹配(注意大小写、拼写一致性) - 自定义解码时需覆盖所有必填字段,避免解码失败
- 枚举方案类型安全性更高,优先推荐;协议方案适合需要统一处理公共属性的场景
内容的提问来源于stack exchange,提问作者Remi
相关产品推荐
相关产品推荐

