// // Common+Ext.swift // Crush // // Created by Leon on 2025/7/11. // import Foundation /// 打印 //func dlog(_ message: T, file: StaticString = #file, method: String = #function, line: Int = #line) { // #if DEBUG // let fileName = (file.description as NSString).lastPathComponent // print("🔹\(fileName) \(method)[\(line)]:\n\(message)\n") // #endif //} func dlog(_ message: T, file: StaticString = #file, method: String = #function, line: Int = #line) { #if DEBUG let fileName = (file.description as NSString).lastPathComponent print("🔹\(fileName) \(method)[\(line)]:\n\(message)\n") #endif } struct EmptyModel: Codable {} // MARK: - DecodableValue enum DecodableValue: Decodable, Encodable { case string(String) case int(Int) case double(Double) case bool(Bool) case array([DecodableValue]) case dictionary([String: DecodableValue]) case null init(from decoder: Decoder) throws { let container = try decoder.singleValueContainer() if container.decodeNil() { self = .null } else if let b = try? container.decode(Bool.self) { self = .bool(b) } else if let i = try? container.decode(Int.self) { self = .int(i) } else if let d = try? container.decode(Double.self) { self = .double(d) } else if let s = try? container.decode(String.self) { self = .string(s) } else if let a = try? container.decode([DecodableValue].self) { self = .array(a) } else if let dict = try? container.decode([String: DecodableValue].self) { self = .dictionary(dict) } else { throw DecodingError.dataCorruptedError( in: container, debugDescription: "Unsupported value" ) } } // MARK: - Encodable func encode(to encoder: Encoder) throws { var container = encoder.singleValueContainer() switch self { case .null: try container.encodeNil() case .bool(let b): try container.encode(b) case .int(let i): try container.encode(i) case .double(let d): try container.encode(d) case .string(let s): try container.encode(s) case .array(let a): try container.encode(a) case .dictionary(let d): try container.encode(d) } } } extension Encodable { func toNonNilDictionary() -> [String: Any] { return Self._toDictionary(value: self) } private static func _toDictionary(value: Any) -> [String: Any] { var result: [String: Any] = [:] let mirror = Mirror(reflecting: value) for child in mirror.children { guard let key = child.label else { continue } let rawValue = child.value let valueMirror = Mirror(reflecting: rawValue) // 处理 Optional 类型 if valueMirror.displayStyle == .optional { if let some = valueMirror.children.first?.value { let processedValue = processValue(some) if !isNil(processedValue) { result[key] = processedValue } } continue } // 处理值 let processedValue = processValue(rawValue) if !isNil(processedValue) { result[key] = processedValue } } return result } private static func processValue(_ value: Any) -> Any { if let rawRepresentable = value as? (any RawRepresentable) { return rawRepresentable.rawValue } // 处理特定类型(如 Sex 枚举) if let sex = value as? Sex { return sex.rawValue } // 处理 Encodable 类型(嵌套结构体或类) if let encodableValue = value as? Encodable { let mirror = Mirror(reflecting: encodableValue) // 处理数组或集合 if mirror.displayStyle == .collection { return mirror.children.compactMap { child in let processed = processValue(child.value) return isNil(processed) ? nil : processed } } // 处理嵌套结构体或类 if mirror.displayStyle == .class || mirror.displayStyle == .struct || mirror.displayStyle == .set { return _toDictionary(value: encodableValue) } } return value } // 辅助函数:检查值是否为 nil private static func isNil(_ value: Any) -> Bool { let mirror = Mirror(reflecting: value) return mirror.displayStyle == .optional && mirror.children.isEmpty } // func toNonNilDictionary() -> [String: Any] { // return Self._toDictionary(value: self) // } // private static func _toDictionary(value: Any) -> [String: Any] { // var result: [String: Any] = [:] // let mirror = Mirror(reflecting: value) // // for child in mirror.children { // guard let key = child.label else { continue } // // let rawValue = child.value // let valueMirror = Mirror(reflecting: rawValue) // // // Optional 类型处理 // if valueMirror.displayStyle == .optional { // if let some = valueMirror.children.first?.value { // result[key] = processValue(some) // } // } else { // result[key] = processValue(rawValue) // } // } // // return result // } // // private static func processValue(_ value: Any) -> Any { // let mirror = Mirror(reflecting: value) // // // 递归处理 Optional // if mirror.displayStyle == .optional { // if let some = mirror.children.first?.value { // return processValue(some) // 递归处理解包后的值 // } else { // return ""// NSNull() // 或者直接 return "" 视你的需求 // } // } // // if let sex = value as? Sex { // return sex.rawValue // } // // if let encodableValue = value as? Encodable { // let mirror = Mirror(reflecting: encodableValue) // // if mirror.displayStyle == .class || mirror.displayStyle == .struct || mirror.displayStyle == .set { // return _toDictionary(value: encodableValue) // } // } // // return value // } // MARK: - part 2 /// 将结构体中指定 key 的字段(如果非 nil)导出为字典 func toPartialDictionary(keys includedKeys: Set = []) -> [String: Any] { return Self._toDictionary(value: self, onlyIncludeKeys: includedKeys) } private static func _toDictionary(value: Any, onlyIncludeKeys: Set? = nil) -> [String: Any] { var result: [String: Any] = [:] let mirror = Mirror(reflecting: value) for child in mirror.children { guard let key = child.label else { continue } if let allowedKeys = onlyIncludeKeys, !allowedKeys.contains(key) { continue } let rawValue = child.value let valueMirror = Mirror(reflecting: rawValue) if valueMirror.displayStyle == .optional { if let some = valueMirror.children.first?.value { result[key] = processValue(some, onlyIncludeKeys: nil) } } else { result[key] = processValue(rawValue, onlyIncludeKeys: nil) } } return result } /// 支持将Sex输出为int private static func processValue(_ value: Any, onlyIncludeKeys: Set?) -> Any { let mirror = Mirror(reflecting: value) if mirror.displayStyle == .struct { return _toDictionary(value: value, onlyIncludeKeys: onlyIncludeKeys) } else if let rawRepresentable = value as? any RawRepresentable { return rawRepresentable.rawValue } else { return value } } } extension Array where Element: Equatable { mutating func removeObj(_ object: Element) { if let index = firstIndex(of: object) { remove(at: index) } } } // MARK: - 通用扩展 extension Optional { /// 是否为 nil var isNil: Bool { self == nil } /// 是否有值 var isSome: Bool { self != nil } } extension Optional where Wrapped == Int { /// 是否大于 0 var isPositive: Bool { (self ?? 0) > 0 } /// 是否为负数 var isNegative: Bool { (self ?? 0) < 0 } /// 是否为 nil 或 0 var isNilOrZero: Bool { self == nil || self == 0 } /// 转字符串(nil → "") var stringValue: String { self.map { "\($0)" } ?? "" } } extension Optional where Wrapped == String { /// 是否为空串或 nil var isNilOrEmpty: Bool { self?.isEmpty ?? true } /// 去除空格后的安全字符串 var trimmed: String { self?.trimmingCharacters(in: .whitespacesAndNewlines) ?? "" } /// 转 Int(失败或 nil 返回 nil) var intValue: Int? { self.flatMap { Int($0) } } /// 转 Double(失败或 nil 返回 nil) var doubleValue: Double? { self.flatMap { Double($0) } } } extension Optional where Wrapped == Bool { /// 默认 false var boolValue: Bool { self ?? false } /// 默认 true var trueOrNil: Bool { self ?? true } } extension Double { func formatted(decimal: Int = 2, asPercent: Bool = false, usesGroupingSeparator: Bool = true) -> String { // let formatter = NumberFormatter() // formatter.minimumFractionDigits = decimal // formatter.maximumFractionDigits = decimal // formatter.numberStyle = .decimal // return formatter.string(from: NSNumber(value: self)) ?? "\(self)" let value = asPercent ? self * 100 : self let formatter = NumberFormatter() formatter.minimumFractionDigits = decimal formatter.maximumFractionDigits = decimal formatter.usesGroupingSeparator = usesGroupingSeparator formatter.numberStyle = .decimal let result = formatter.string(from: NSNumber(value: value)) ?? "\(value)" return asPercent ? result + "%" : result } } extension CGFloat { func formatted(decimal: Int = 2, asPercent: Bool = false, usesGroupingSeparator: Bool = true) -> String { let value = asPercent ? self * 100 : self let formatter = NumberFormatter() formatter.minimumFractionDigits = decimal formatter.maximumFractionDigits = decimal formatter.usesGroupingSeparator = usesGroupingSeparator formatter.numberStyle = .decimal let result = formatter.string(from: NSNumber(value: value)) ?? "\(value)" return asPercent ? result + "%" : result } } extension Dictionary { func merged(with other: Dictionary, preferNew: Bool = true) -> Dictionary { return self.merging(other) { old, new in preferNew ? new : old } } }