Swift 的属性包装器是语法糖,
他可以让代码更加的声明式
定义属性的时候,把规则写进去
属性包装器, 主要使用 wrappedValue, 有时候用到 projectedValue
问题:
存在这样的一个结构体:
struct Figure: Decodable {
var name: String
var age: Int
}
后端传过来,一个 json,
要求 json 对应的内容,长这样,
能够解析出来
var dict: [String: Any] = ["name": "666", "age": 666]
json 对应的内容,长这样,
也可解析出来
dict = ["name": 666, "age": "666"]
问题,解码一个属性,他的数据可能有多种类型
技巧一,使用属性包装器
提供解码方法
static var losslessDecodableTypes:
, 是一个解码方法的集合
系统提供的 LosslessStringConvertible
协议,可以让字符串,转值
public typealias LosslessStringCodable = LosslessStringConvertible & Codable
public struct LosslessDefaultStrategy{
public static var losslessDecodableTypes: [(Decoder) -> LosslessStringCodable?] {
@inline(__always)
func decode(_: T.Type) -> (Decoder) -> LosslessStringCodable? {
return { try? T.init(from: $0) }
}
return [
decode(String.self),
decode(Int.self),
decode(Double.self),
decode(Float.self)
]
}
}
通过属性包装器,
- 解码的时候,先使用属性对应类型,进行解码
- 出错了,再使用上面指定的各种类型的解码器,都解码一遍
上面的
decode(Float.self)
, 可以删因为 Double 的精度更好,走不到使用 Float 解码
public protocol LosslessDecodingStrategy {
associatedtype Value: LosslessStringCodable
/// An ordered list of decodable scenarios used to infer the encoded type
static var losslessDecodableTypes: [(Decoder) -> LosslessStringCodable?] { get }
}
@propertyWrapper
public struct LosslessValueCodable: Codable {
private let type: LosslessStringCodable.Type
public var wrappedValue: Strategy.Value
public init(wrappedValue: Strategy.Value) {
self.wrappedValue = wrappedValue
self.type = Strategy.Value.self
}
public init(from decoder: Decoder) throws {
do {
self.wrappedValue = try Strategy.Value.init(from: decoder)
self.type = Strategy.Value.self
} catch let error {
for block in Strategy.losslessDecodableTypes{
if let rawVal = block(decoder), let value = Strategy.Value.init("\(rawVal)"){
self.wrappedValue = value
self.type = Swift.type(of: rawVal)
return
}
}
throw error
}
}
}
因为上文大量使用范型,
定义,使用默认解码方式 LosslessDefaultStrategy
, 的属性装饰器
public typealias LosslessValue = LosslessValueCodable> where T: LosslessStringCodable
调用部分
属性定义
struct Figure: Decodable {
@LosslessValue var name: String
@LosslessValue var age: Int
}
数据解码
var dict: [String: Any] = ["name": "666", "age": 666]
dict = ["name": 666, "age": "666"]
if let jsonData = try? JSONSerialization.data(withJSONObject: dict, options: []), let model = try? JSONDecoder().decode(Figure.self, from: jsonData){
print(model.name, " - ", model.age)
}
技巧 2 ,糙一些,制定自定义解码
代码:
代码很幼稚,
使用各种类型,去解码,
没有使用范型,使用枚举的多种类型,去获取值
使用值的时候,就将 self
拆包,可能会强转
不声明式, 看代码,不知道属性的具体类型
调用的时候,根据业务,去使用对应类型的值
属性包装器更加优雅,直接使用,隐藏了 wrappedValue
enum VariousTypeErr: Error {
case miss
}
enum VariousType: Decodable{
case aDouble(Double), aFloat(Float), aInt(Int), aString(String)
init(from decoder: Decoder) throws {
if let int = try? decoder.singleValueContainer().decode(Int.self) {
self = .aInt(int)
return
}
if let value = try? decoder.singleValueContainer().decode(Double.self) {
self = .aDouble(value)
return
}
if let gotIt = try? decoder.singleValueContainer().decode(Float.self) {
self = .aFloat(gotIt)
return
}
if let text = try? decoder.singleValueContainer().decode(String.self) {
self = .aString(text)
return
}
throw VariousTypeErr.miss
}
var strVal: String{
switch self {
case .aDouble(let val):
return String(val)
case .aFloat(let val):
return String(val)
case .aInt(let val):
return String(val)
case .aString(let val):
return val
}
}
var intVal: Int{
switch self {
case .aDouble(let val):
return Int(val)
case .aFloat(let val):
return Int(val)
case .aInt(let val):
return val
case .aString(let val):
return Int(val) ?? 0
}
}
}
调用:
struct Person: Decodable {
var name: VariousType
var age: VariousType
}
var dict: [String: Any] = ["name": "666", "age": 666]
dict = ["name": 666, "age": "666"]
if let jsonData = try? JSONSerialization.data(withJSONObject: dict, options: []), let model = try? JSONDecoder().decode(Person.self, from: jsonData){
print(model.name.strVal, " - ", model.age.intVal)
}