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

SQLite.swift中Codable功能与Int枚举兼容问题求助

解决SQLite.swift Codable插入枚举时的编码错误

这个问题我之前也碰到过,根源是SQLite.swift对Codable枚举的默认处理逻辑和你的需求不匹配——它会把枚举当成嵌套Codable类型编码成JSON片段,但你的数据库列是Int类型,所以才会抛出Top-level MyEnum encoded as number JSON fragment的错误。下面给你两种优雅的解决办法,都能避免回到手动写序列化/反序列化的老代码:

方案1:让枚举适配SQLite.swift的Value协议(推荐)

SQLite.swift的Value协议定义了Swift类型和数据库列类型之间的转换规则。对于你的Int-backed枚举,只需要简单实现这个协议,就能让SQLite.swift自动处理枚举和数据库Int列的映射,完全不用修改Codable逻辑。

首先修改你的枚举:

enum MyEnum: Int, Codable, Value {
    case FIRSTVAL = 0, SECONDVAL = 1
    
    // 实现Value协议的三个要求
    static var declaredDatatype: String {
        // 复用Int的数据库类型定义
        return Int.declaredDatatype
    }
    
    static func fromDatatypeValue(_ datatypeValue: Int) -> MyEnum {
        // 从数据库的Int值转成枚举
        return MyEnum(rawValue: datatypeValue)!
    }
    
    var datatypeValue: Int {
        // 把枚举转成对应的Int rawValue存入数据库
        return rawValue
    }
}

然后更新MyClass里的列定义,把Expression<Int>改成Expression<MyEnum>:

class MyClass : Codable {
    static let tableRef = Table("MyTable")
    static let idColumn = Expression<Int>("MyTableId");
    static let nameColumn = Expression<String>("Name")
    // 改成Expression<MyEnum>
    static let myEnumValColumn = Expression<MyEnum>("MyEnumVal")
    
    // 其他代码不变...
    var MyTableId : Int!
    var Name : String
    var MyEnumVal : MyEnum
}

这样修改后,你原来的插入代码try db.run(MyClass.tableRef.insert(anInstanceOfMyClass))就能直接正常工作了。SQLite.swift会自动把枚举转换成对应的Int值存入数据库,查询时也会自动把Int值转回枚举类型,全程不需要手动干预。

方案2:自定义MyClass的Codable实现

如果你不想修改枚举的定义,也可以在MyClass里手动实现Codable的编码和解码方法,强制把枚举转换成rawValue处理:

class MyClass : Codable {
    static let tableRef = Table("MyTable")
    static let idColumn = Expression<Int>("MyTableId");
    static let nameColumn = Expression<String>("Name")
    static let myEnumValColumn = Expression<Int>("MyEnumVal")
    
    static func createTable(_ db : Connection) throws {
        try db.run(tableRef.create(ifNotExists: true) { t in
            t.column(idColumn, primaryKey: true)
            t.column(nameColumn)
            t.column(myEnumValColumn)
        })
    }
    
    var MyTableId : Int!
    var Name : String
    var MyEnumVal : MyEnum
    
    // 定义CodingKeys,对应数据库列名
    enum CodingKeys: String, CodingKey {
        case MyTableId, Name, MyEnumVal
    }
    
    // 自定义编码逻辑:把枚举转成rawValue
    func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(MyTableId, forKey: .MyTableId)
        try container.encode(Name, forKey: .Name)
        try container.encode(MyEnumVal.rawValue, forKey: .MyEnumVal)
    }
    
    // 自定义解码逻辑:从rawValue转成枚举
    required init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        MyTableId = try container.decode(Int.self, forKey: .MyTableId)
        Name = try container.decode(String.self, forKey: .Name)
        
        let rawValue = try container.decode(Int.self, forKey: .MyEnumVal)
        guard let enumValue = MyEnum(rawValue: rawValue) else {
            throw DecodingError.dataCorruptedError(
                forKey: .MyEnumVal,
                in: container,
                debugDescription: "无效的MyEnum raw值: \(rawValue)"
            )
        }
        MyEnumVal = enumValue
    }
}

这种方式下,SQLite.swift会使用你自定义的Codable逻辑,把枚举的rawValue存入Int列,同样不需要回到你之前写的手动insert和init(row)方法。

方案对比

  • 方案1更优雅,一次实现后,所有使用这个枚举的SQLite.swift列都能自动适配,代码复用性更高。
  • 方案2适合你无法修改枚举定义的场景,但需要在每个包含该枚举的Codable类中重复编写编码解码逻辑。

内容的提问来源于stack exchange,提问作者Neil Mountford

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 03:26:40