You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.24 20:34:52