Visual_Novel_iOS/crush/Crush/Src/Utils/Extensions/CommonExt.swift

356 lines
11 KiB
Swift
Raw Normal View History

2025-10-09 10:29:35 +00:00
//
// 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
}
/// Sexint
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 }
}
}