close

自訂屬性包裝器(property wrapper) ,

找不到key、String, Int, Double, CGFloat 嘗試轉型,都失敗會給預設值。


protocol DefaultValue {
    associatedtype Value: Decodable
    static var defaultValue: Value { get }
}

extension Int {
    struct Zero: DefaultValue {
        static var defaultValue: Int { 0 }
    }
}

extension Bool {

    struct True: DefaultValue {
        static var defaultValue: Bool { true }
    }
    
    struct False: DefaultValue {
        static var defaultValue: Bool { false }
    }

}

extension Double {
    struct Zero: DefaultValue {
        static var defaultValue: Double { 0.0 }
    }
}

extension CGFloat {
    struct Zero: DefaultValue {
        static var defaultValue: Double { 0.0 }
    }
}

extension String {
    struct Empty: DefaultValue {
        static var defaultValue: String { "" }
    }
}

extension Array where Element: Decodable {
    
    
    /// 解失敗給預設值 []
    /// ```
    /// /// 廣告相關
    /// @Default or @Default
    /// var ad: [Ad]
    /// ```
    struct Empty: DefaultValue {
        static var defaultValue: Array { [] }
    }
}

/// 用在JSON 反序列化 (套在欲decode的屬性上)
///  ```
/// /// 廣告相關
/// @Default<[Ad]> or @Default
/// var ad: [Ad]
///  ```
///
///  ```
/// /// e.g. 326532
/// @Default or @Default.EmptyString
/// var article_id: String
///  ```
/// - 支援 Int, Bool, Double, CGFloat, String, Array
/// - 找不到key,設成預設值
/// - 型態不符(String, Int, Double, CGFloat),會嘗試轉型
@propertyWrapper
struct Default {
    var wrappedValue: T.Value
}

/// MARK - 縮短泛型方便使用
extension Default {
    typealias True = Default
    typealias False = Default
    typealias EmptyString = Default
}

extension Default: Decodable {

    init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()
        
        wrappedValue = {
            switch T.Value.self {
                case is String.Type:
                    
                    // Int, Double to Stirng
                    let value: String? = {
                        if let value = (try? container.decode(String.self)) {
                            return value
                        } else if let value = (try? container.decode(Int.self)) {
                            return String(value)
                        } else if let value = (try? container.decode(Double.self)) {
                            return String(value)
                        } else {
                            return nil
                        }
                    }()
                    
                    return value as? T.Value ?? T.defaultValue // 轉型失敗有預設值
                    
                case is Int.Type:

                    // Stirng, Double to Int
                    let value: Int? = {
                        if let value = (try? container.decode(Int.self)) {
                            return value
                        } else if let value = (try? container.decode(String.self)) {
                            return Int(value)
                        } else if let value = (try? container.decode(Double.self)) {
                            return Int(value)
                        } else {
                            return nil
                        }
                    }()
                    
                    return value as? T.Value ?? T.defaultValue // 轉型失敗有預設值
                    
                case is Double.Type:
                    
                    // Stirng, Int to Double
                    let value: Double? = {
                        if let value = (try? container.decode(Double.self)) {
                            return value
                        } else if let value = (try? container.decode(String.self)) {
                            return Double(value)
                        } else if let value = (try? container.decode(Int.self)) {
                            return Double(value)
                        } else {
                            return nil
                        }
                    }()
                    
                    return value as? T.Value ?? T.defaultValue // 轉型失敗有預設值
                    
                case is CGFloat.Type:

                    // Stirng, Int to CGFloat
                    let value: CGFloat? = {
                        if let value = (try? container.decode(CGFloat.self)) {
                            return value
                        } else if let value = (try? container.decode(String.self)) {
                            return CGFloat(value)
                        } else if let value = (try? container.decode(Int.self)) {
                            return CGFloat(value)
                        } else {
                            return nil
                        }
                    }()
                    
                    return value as? T.Value ?? T.defaultValue // 轉型失敗有預設值
                    
                default:
                    return (try? container.decode(T.Value.self)) ?? T.defaultValue // decode失敗有預設值
            }
        }()
    }
}

extension KeyedDecodingContainer {
    func decode(_ type: Default.Type, forKey key: Key) throws -> Default where T: DefaultValue {
        
        // 沒Key時給預設值
        try decodeIfPresent(type, forKey: key) ?? Default(wrappedValue: T.defaultValue)
    }
}


https://onevcat.com/2020/11/codable-default/

https://www.tpisoftware.com/tpu/articleDetails/2272

arrow
arrow
    全站熱搜

    小賢 發表在 痞客邦 留言(0) 人氣()