Files
WerkoutIOS/iphone/Werkout_ios/Extensions.swift

194 lines
5.7 KiB
Swift

//
// Extensions.swift
// Werkout_ios
//
// Created by Trey Tartt on 6/20/23.
//
import Foundation
import UIKit
import SwiftUI
private enum DateFormatterCache {
static let lock = NSLock()
static let serverDateFormatter: DateFormatter = {
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssX"
formatter.locale = Locale(identifier: "en_US_POSIX")
return formatter
}()
static let plannedDateFormatter: DateFormatter = {
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd"
formatter.locale = Locale(identifier: "en_US_POSIX")
return formatter
}()
static let weekDayFormatter: DateFormatter = {
let formatter = DateFormatter()
formatter.dateFormat = "EEE"
formatter.locale = Locale(identifier: "en_US_POSIX")
return formatter
}()
static let monthFormatter: DateFormatter = {
let formatter = DateFormatter()
formatter.dateFormat = "MMM"
formatter.locale = Locale(identifier: "en_US_POSIX")
return formatter
}()
static let dayFormatter: DateFormatter = {
let formatter = DateFormatter()
formatter.dateFormat = "d"
formatter.locale = Locale(identifier: "en_US_POSIX")
return formatter
}()
static func withLock<T>(_ block: () -> T) -> T {
lock.lock()
defer { lock.unlock() }
return block()
}
}
private enum DurationFormatterCache {
static let lock = NSLock()
static let formatter: DateComponentsFormatter = {
let formatter = DateComponentsFormatter()
formatter.allowedUnits = [.hour, .minute, .second, .nanosecond]
return formatter
}()
static func string(from seconds: Double, style: DateComponentsFormatter.UnitsStyle) -> String {
lock.lock()
defer { lock.unlock() }
formatter.unitsStyle = style
return formatter.string(from: seconds) ?? ""
}
}
extension Dictionary {
func percentEncoded() -> Data? {
map { key, value in
let escapedKey = "\(key)".addingPercentEncoding(withAllowedCharacters: .urlQueryValueAllowed) ?? ""
let escapedValue = "\(value)".addingPercentEncoding(withAllowedCharacters: .urlQueryValueAllowed) ?? ""
return escapedKey + "=" + escapedValue
}
.joined(separator: "&")
.data(using: .utf8)
}
}
extension CharacterSet {
static let urlQueryValueAllowed: CharacterSet = {
let generalDelimitersToEncode = ":#[]@" // does not include "?" or "/" due to RFC 3986 - Section 3.4
let subDelimitersToEncode = "!$&'()*+,;="
var allowed: CharacterSet = .urlQueryAllowed
allowed.remove(charactersIn: "\(generalDelimitersToEncode)\(subDelimitersToEncode)")
return allowed
}()
}
extension Date {
var timeFormatForUpload: String {
DateFormatterCache.withLock {
DateFormatterCache.serverDateFormatter.string(from: self)
}
}
}
extension String {
var dateFromServerDate: Date? {
DateFormatterCache.withLock {
DateFormatterCache.serverDateFormatter.date(from: self)
}
}
var plannedDate: Date? {
DateFormatterCache.withLock {
DateFormatterCache.plannedDateFormatter.date(from: self)
}
}
}
extension Date {
func get(_ components: Calendar.Component..., calendar: Calendar = Calendar.current) -> DateComponents {
return calendar.dateComponents(Set(components), from: self)
}
func get(_ component: Calendar.Component, calendar: Calendar = Calendar.current) -> Int {
return calendar.component(component, from: self)
}
var formatForPlannedWorkout: String {
DateFormatterCache.withLock {
DateFormatterCache.plannedDateFormatter.string(from: self)
}
}
var weekDay: String {
DateFormatterCache.withLock {
DateFormatterCache.weekDayFormatter.string(from: self)
}
}
var monthString: String {
DateFormatterCache.withLock {
DateFormatterCache.monthFormatter.string(from: self)
}
}
var dateString: String {
DateFormatterCache.withLock {
DateFormatterCache.dayFormatter.string(from: self)
}
}
}
extension Bundle {
public var icon: UIImage? {
if let icons = infoDictionary?["CFBundleIcons"] as? [String: Any],
let primaryIcon = icons["CFBundlePrimaryIcon"] as? [String: Any],
let iconFiles = primaryIcon["CFBundleIconFiles"] as? [String],
let lastIcon = iconFiles.last {
return UIImage(named: lastIcon)
}
return nil
}
}
extension Double {
/*
10000.asString(style: .positional) // 2:46:40
10000.asString(style: .abbreviated) // 2h 46m 40s
10000.asString(style: .short) // 2 hr, 46 min, 40 sec
10000.asString(style: .full) // 2 hours, 46 minutes, 40 seconds
10000.asString(style: .spellOut) // two hours, forty-six minutes, forty seconds
10000.asString(style: .brief) // 2hr 46min 40sec
*/
func asString(style: DateComponentsFormatter.UnitsStyle) -> String {
DurationFormatterCache.string(from: self, style: style)
}
}
extension View {
func cornerRadius(_ radius: CGFloat, corners: UIRectCorner) -> some View {
clipShape( RoundedCorner(radius: radius, corners: corners) )
}
}
struct RoundedCorner: Shape {
var radius: CGFloat = .infinity
var corners: UIRectCorner = .allCorners
func path(in rect: CGRect) -> Path {
let path = UIBezierPath(roundedRect: rect, byRoundingCorners: corners, cornerRadii: CGSize(width: radius, height: radius))
return Path(path.cgPath)
}
}