114 lines
3.8 KiB
Swift
114 lines
3.8 KiB
Swift
import UIKit
|
|
|
|
struct QLog {
|
|
private init() {}
|
|
static func m(_ message: String) { write("", message) }
|
|
static func Info(_ message: String) { write("[INFO] ", message) }
|
|
#if DEBUG
|
|
static func Debug(_ message: String) { write("[DEBUG] ", message) }
|
|
#else
|
|
static func Debug(_ _: String) {}
|
|
#endif
|
|
static func Error(_ message: String) { write("[ERROR] ", message) }
|
|
static func Warning(_ message: String) { write("[WARN] ", message) }
|
|
private static func write(_ tag: String, _ message: String) {
|
|
print(String(format: "%1.3f %@%@", Date().timeIntervalSince1970, tag, message))
|
|
}
|
|
}
|
|
|
|
extension Collection {
|
|
subscript(ifExist i: Index?) -> Iterator.Element? {
|
|
guard let i = i else { return nil }
|
|
return indices.contains(i) ? self[i] : nil
|
|
}
|
|
}
|
|
|
|
var listOfSLDs: [String : [String : Bool]] = {
|
|
let path = Bundle.main.url(forResource: "third-level", withExtension: "txt")
|
|
let content = try! String(contentsOf: path!)
|
|
var res: [String : [String : Bool]] = [:]
|
|
content.enumerateLines { line, _ in
|
|
let dom = line.split(separator: ".")
|
|
let tld = String(dom.first!)
|
|
let sld = String(dom.last!)
|
|
if res[tld] == nil { res[tld] = [:] }
|
|
res[tld]![sld] = true
|
|
}
|
|
return res
|
|
}()
|
|
|
|
extension String {
|
|
/// Check if string is equal to `domain` or ends with `.domain`
|
|
func isSubdomain(of domain: String) -> Bool { self == domain || self.hasSuffix("." + domain) }
|
|
/// Split string into top level domain part and host part
|
|
func splitDomainAndHost() -> (domain: String, host: String?) {
|
|
let lastChr = last?.asciiValue ?? 0
|
|
guard lastChr > UInt8(ascii: "9") || lastChr < UInt8(ascii: "0") else { // IP address
|
|
return (domain: "# IP connection", host: self)
|
|
}
|
|
var parts = components(separatedBy: ".")
|
|
guard let tld = parts.popLast(), let sld = parts.popLast() else {
|
|
return (domain: self, host: nil) // no subdomains, just plain SLD
|
|
}
|
|
var ending = sld + "." + tld
|
|
if listOfSLDs[tld]?[sld] ?? false, let rld = parts.popLast() {
|
|
ending = rld + "." + ending
|
|
}
|
|
return (domain: ending, host: parts.joined(separator: "."))
|
|
}
|
|
/// Returns `true` if String matches list of known second level domains (e.g., `co.uk`).
|
|
func isKnownSLD() -> Bool {
|
|
let parts = components(separatedBy: ".")
|
|
return parts.count == 2 && listOfSLDs[parts.last!]?[parts.first!] ?? false
|
|
}
|
|
}
|
|
|
|
extension Timer {
|
|
@discardableResult static func repeating(_ interval: TimeInterval, call selector: Selector, on target: Any, userInfo: Any? = nil) -> Timer {
|
|
Timer.scheduledTimer(timeInterval: interval, target: target, selector: selector,
|
|
userInfo: userInfo, repeats: true)
|
|
}
|
|
}
|
|
|
|
extension DateFormatter {
|
|
convenience init(withFormat: String) {
|
|
self.init()
|
|
dateFormat = withFormat
|
|
}
|
|
func with(format: String) -> Self {
|
|
dateFormat = format
|
|
return self
|
|
}
|
|
func string(from ts: Timestamp) -> String {
|
|
string(from: Date.init(timeIntervalSince1970: Double(ts)))
|
|
}
|
|
}
|
|
|
|
struct TimeFormat {
|
|
static func from(_ duration: Timestamp) -> String {
|
|
String(format: "%02d:%02d", duration / 60, duration % 60)
|
|
}
|
|
static func from(_ duration: TimeInterval, millis: Bool = false) -> String {
|
|
let t = Int(duration)
|
|
if millis {
|
|
let mil = Int(duration * 1000) % 1000
|
|
return String(format: "%02d:%02d.%03d", t / 60, t % 60, mil)
|
|
}
|
|
return String(format: "%02d:%02d", t / 60, t % 60)
|
|
}
|
|
static func since(_ date: Date, millis: Bool = false) -> String {
|
|
from(Date().timeIntervalSince(date), millis: millis)
|
|
}
|
|
}
|
|
|
|
extension UIColor {
|
|
static var sysBg: UIColor { get { if #available(iOS 13.0, *) { return .systemBackground } else { return .white } }}
|
|
static var sysFg: UIColor { get { if #available(iOS 13.0, *) { return .label } else { return .black } }}
|
|
}
|
|
|
|
extension UIEdgeInsets {
|
|
init(all: CGFloat = 0, top: CGFloat? = nil, left: CGFloat? = nil, bottom: CGFloat? = nil, right: CGFloat? = nil) {
|
|
self.init(top: top ?? all, left: left ?? all, bottom: bottom ?? all, right: right ?? all)
|
|
}
|
|
}
|