关于ios:解码多种可能类型的两种技巧属性包装器

29次阅读

共计 3559 个字符,预计需要花费 9 分钟才能阅读完成。

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<Value: LosslessStringCodable>{public static var losslessDecodableTypes: [(Decoder) -> LosslessStringCodable?] {@inline(__always)
        func decode<T: LosslessStringCodable>(_: 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<Strategy: LosslessDecodingStrategy>: 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<T> = LosslessValueCodable<LosslessDefaultStrategy<T>> 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)
        }

github repo

正文完
 0