Swift 4中如何用标准Decodable解析Bool/Int类型的属性?
在Swift 4中用Decodable解析混合布尔/整数类型的属性
这种服务器返回属性类型不统一的情况确实挺头疼的——一会儿是布尔值true/false,一会儿又变成整数0/1,默认的Decodable肯定直接报错。不过咱们完全可以用Swift标准库的Codable协议自定义解码逻辑来搞定,不用装第三方库。
方案一:直接在Model类里自定义解码逻辑(推荐统一类型)
如果业务上希望把这个属性统一成布尔值(毕竟0和false、1和true语义一致),可以修改你的MyOffer类,重写init(from decoder: Decoder)方法来处理类型兼容:
final class MyOffer: Codable { var id = 0 var pickupAsap: Bool? // 统一为布尔类型,更符合业务逻辑 enum CodingKeys: String, CodingKey { case id case pickupAsap = "pickup_asap" } required init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) // 正常解析固定类型的id id = try container.decode(Int.self, forKey: .id) // 处理pickupAsap的类型兼容 do { // 先尝试按布尔值解码(对应服务器返回true/false的情况) pickupAsap = try container.decode(Bool.self, forKey: .pickupAsap) } catch DecodingError.typeMismatch { // 类型不匹配时,说明返回的是整数,转成布尔值(0→false,非0→true) let intValue = try container.decode(Int.self, forKey: .pickupAsap) pickupAsap = intValue != 0 } catch { // 其他解码错误直接抛出,保持默认的缺失处理逻辑 throw error } } }
方案二:通用的混合类型枚举(灵活复用)
如果你有多个属性都存在这种类型不统一的情况,可以写一个通用的BoolOrInt枚举,实现Decodable协议,这样可以把兼容逻辑复用起来:
// 定义一个能兼容布尔和整数的枚举 enum BoolOrInt: Decodable { case bool(Bool) case int(Int) init(from decoder: Decoder) throws { let container = try decoder.singleValueContainer() // 先尝试解码布尔值,失败再试整数 if let boolVal = try? container.decode(Bool.self) { self = .bool(boolVal) } else if let intVal = try? container.decode(Int.self) { self = .int(intVal) } else { // 既不是布尔也不是整数,抛出解码错误 throw DecodingError.typeMismatch( BoolOrInt.self, DecodingError.Context( codingPath: decoder.codingPath, debugDescription: "Expected value to be either Bool or Int" ) ) } } // 方便快速获取布尔值 var boolValue: Bool { switch self { case .bool(let val): return val case .int(let val): return val != 0 } } // 方便快速获取整数值 var intValue: Int { switch self { case .bool(let val): return val ? 1 : 0 case .int(let val): return val } } }
然后在MyOffer里直接用这个枚举类型:
final class MyOffer: Codable { var id = 0 var pickupAsap: BoolOrInt? // 使用通用枚举类型 enum CodingKeys: String, CodingKey { case id case pickupAsap = "pickup_asap" } }
使用的时候就可以通过pickupAsap?.boolValue或者pickupAsap?.intValue轻松拿到统一格式的值,非常灵活。
小提示
如果业务上确实需要保留原始的类型(既可能是Bool也可能是Int),也可以把属性类型设为Any?,但后续使用时需要做类型判断,代码会繁琐很多,所以更推荐统一成单一类型,减少后续的类型处理成本。
内容的提问来源于stack exchange,提问作者Yurii Radchenko
相关产品推荐
相关产品推荐

