159 lines
		
	
	
		
			4.2 KiB
		
	
	
	
		
			Swift
		
	
	
		
		
			
		
	
	
			159 lines
		
	
	
		
			4.2 KiB
		
	
	
	
		
			Swift
		
	
	
|  | // | ||
|  | //  Utilities.swift | ||
|  | //  GuruAnalytics_iOS | ||
|  | // | ||
|  | //  Created by mayue on 2022/11/4. | ||
|  | // | ||
|  | 
 | ||
|  | import Foundation | ||
|  | import RxSwift | ||
|  | 
 | ||
|  | internal extension TimeInterval { | ||
|  |      | ||
|  |     var int64Ms: Int64 { | ||
|  |         return Int64(self * 1000) | ||
|  |     } | ||
|  |      | ||
|  | } | ||
|  | 
 | ||
|  | internal extension Date { | ||
|  |      | ||
|  |     var msSince1970: Int64 { | ||
|  |         timeIntervalSince1970.int64Ms | ||
|  |     } | ||
|  |      | ||
|  |     static var absoluteTimeMs: Int64 { | ||
|  |         return CACurrentMediaTime().int64Ms | ||
|  |     } | ||
|  |      | ||
|  | } | ||
|  | 
 | ||
|  | internal extension Dictionary { | ||
|  |      | ||
|  |     func jsonString(prettify: Bool = false) -> String? { | ||
|  |         guard JSONSerialization.isValidJSONObject(self) else { return nil } | ||
|  |         let options = (prettify == true) ? JSONSerialization.WritingOptions.prettyPrinted : JSONSerialization.WritingOptions() | ||
|  |         guard let jsonData = try? JSONSerialization.data(withJSONObject: self, options: options) else { return nil } | ||
|  |         return String(data: jsonData, encoding: .utf8) | ||
|  |     } | ||
|  |      | ||
|  | } | ||
|  | 
 | ||
|  | internal extension String { | ||
|  |     func convertToDictionary() -> [String: Any]? { | ||
|  |         if let data = data(using: .utf8) { | ||
|  |             return (try? JSONSerialization.jsonObject(with: data, options: [])) as? [String: Any] | ||
|  |         } | ||
|  |         return nil | ||
|  |     } | ||
|  |      | ||
|  |     mutating func deletePrefix(_ prefix: String) { | ||
|  |         guard hasPrefix(prefix) else { return } | ||
|  |         if #available(iOS 16.0, *) { | ||
|  |             trimPrefix(prefix) | ||
|  |         } else { | ||
|  |             removeFirst(prefix.count) | ||
|  |         } | ||
|  |     } | ||
|  |      | ||
|  |     mutating func trimmed(in set: CharacterSet) { | ||
|  |         self = trimmingCharacters(in: set) | ||
|  |     } | ||
|  | } | ||
|  | 
 | ||
|  | internal extension Array { | ||
|  |     func chunked(into size: Int) -> [[Element]] { | ||
|  |         return stride(from: 0, to: count, by: size).map { | ||
|  |             Array(self[$0 ..< Swift.min($0 + size, count)]) | ||
|  |         } | ||
|  |     } | ||
|  | } | ||
|  | 
 | ||
|  | internal class SafeValue<T> { | ||
|  |      | ||
|  |     private var _value: T | ||
|  |      | ||
|  |     private let queue = DispatchQueue(label: "com.guru.analytics.safe.value.reader.writer.queue", attributes: .concurrent) | ||
|  |     private let group = DispatchGroup() | ||
|  |      | ||
|  |     internal init(_ value: T) { | ||
|  |         _value = value | ||
|  |     } | ||
|  |      | ||
|  |     internal func setValue(_ value: T) { | ||
|  |         queue.async(group: group, execute: .init(flags: .barrier, block: { [weak self] in | ||
|  |             self?._value = value | ||
|  |         })) | ||
|  |     } | ||
|  |      | ||
|  |     internal func getValue(_ valueBlock: @escaping ((T) -> Void)) { | ||
|  |         queue.async(group: group, execute: .init(block: { [weak self] in | ||
|  |             guard let `self` = self else { return } | ||
|  |             valueBlock(self._value) | ||
|  |         })) | ||
|  |     } | ||
|  |      | ||
|  |     internal var singleValue: Single<T> { | ||
|  |         return Single.create { [weak self] subscriber in | ||
|  |              | ||
|  |             self?.getValue { value in | ||
|  |                 subscriber(.success(value)) | ||
|  |             } | ||
|  |              | ||
|  |             return Disposables.create() | ||
|  |         } | ||
|  |     } | ||
|  | } | ||
|  | 
 | ||
|  | internal extension SafeValue where T == Dictionary<String, String> { | ||
|  |      | ||
|  |     func mergeValue(_ value: T) -> Single<Void> { | ||
|  |         return .create { [weak self] subscriber in | ||
|  |             guard let `self` = self else { | ||
|  |                 subscriber(.failure( | ||
|  |                     NSError(domain: "safevalue", code: 0, userInfo: [NSLocalizedDescriptionKey : "safevalue object is released"]) | ||
|  |                 )) | ||
|  |                 return Disposables.create() | ||
|  |             } | ||
|  |             self.getValue { currentValue in | ||
|  |                 let newValue = currentValue.merging(value) { _, new in new } | ||
|  |                 self.setValue(newValue) | ||
|  |                 subscriber(.success(())) | ||
|  |             } | ||
|  |              | ||
|  |             return Disposables.create() | ||
|  |         } | ||
|  |     } | ||
|  | } | ||
|  | 
 | ||
|  | internal extension SafeValue where T == Array<String> { | ||
|  |      | ||
|  |     func appendValue(_ value: T) { | ||
|  |         getValue { [weak self] v in | ||
|  |             var currentValue = v | ||
|  |             currentValue.append(contentsOf: value) | ||
|  |             self?.setValue(currentValue) | ||
|  |         } | ||
|  |     } | ||
|  |      | ||
|  |     func removeAll(where shouldBeRemoved: @escaping (Array<String>.Element) -> Bool) { | ||
|  |         getValue { [weak self] v in | ||
|  |             var currentValue = v | ||
|  |             currentValue.removeAll(where: shouldBeRemoved) | ||
|  |             self?.setValue(currentValue) | ||
|  |         } | ||
|  |     } | ||
|  |      | ||
|  | } | ||
|  | 
 | ||
|  | internal extension Character { | ||
|  |      | ||
|  |     var isAlphabetic: Bool { | ||
|  |         return (self >= "a" && self <= "z") || (self >= "A" && self <= "Z") | ||
|  |     } | ||
|  |      | ||
|  |     var isDigit: Bool { | ||
|  |         return self >= "0" && self <= "9" | ||
|  |     } | ||
|  | } |