356 lines
11 KiB
Swift
356 lines
11 KiB
Swift
//
|
||
// Common+Ext.swift
|
||
// Crush
|
||
//
|
||
// Created by Leon on 2025/7/11.
|
||
//
|
||
|
||
import Foundation
|
||
|
||
/// 打印
|
||
//func dlog<T>(_ 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<T>(_ 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> = []) -> [String: Any] {
|
||
return Self._toDictionary(value: self, onlyIncludeKeys: includedKeys)
|
||
}
|
||
|
||
private static func _toDictionary(value: Any, onlyIncludeKeys: Set<String>? = 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<String>?) -> 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 }
|
||
}
|
||
}
|