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

356 lines
11 KiB
Swift
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//
// 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 }
}
}