import Foundation import HealthKit enum Formatters { static func percent(for number: Double) -> String { let formater = NumberFormatter() formater.numberStyle = .percent return formater.string(for: number)! } static func timeFor(minutes: Int) -> String { let formater = DateComponentsFormatter() formater.unitsStyle = .abbreviated formater.allowedUnits = [.hour, .minute] return formater.string(from: TimeInterval(minutes * 60))! } } extension Formatter { static let iso8601withFractionalSeconds: ISO8601DateFormatter = { let formatter = ISO8601DateFormatter() formatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds] return formatter }() static let iso8601: ISO8601DateFormatter = { let formatter = ISO8601DateFormatter() formatter.formatOptions = [.withInternetDateTime] return formatter }() static let decimalFormatterWithTwoFractionDigits: NumberFormatter = { let formatter = NumberFormatter() formatter.numberStyle = .decimal formatter.maximumFractionDigits = 2 return formatter }() static let dateFormatter: DateFormatter = { let dateFormatter = DateFormatter() dateFormatter.timeStyle = .short return dateFormatter }() static let decimalFormatterWithOneFractionDigit: NumberFormatter = { let formatter = NumberFormatter() formatter.numberStyle = .decimal formatter.maximumFractionDigits = 1 return formatter }() static let integerFormatter: NumberFormatter = { let formatter = NumberFormatter() formatter.numberStyle = .decimal formatter.maximumFractionDigits = 0 return formatter }() static func glucoseFormatter(for units: GlucoseUnits) -> NumberFormatter { let formatter = NumberFormatter() formatter.numberStyle = .decimal formatter.roundingMode = .halfUp switch units { case .mmolL: formatter.maximumFractionDigits = 1 case .mgdL: formatter.maximumFractionDigits = 0 } return formatter } static let bolusFormatter: NumberFormatter = { let formatter = NumberFormatter() formatter.numberStyle = .decimal formatter.minimumIntegerDigits = 0 formatter.maximumFractionDigits = 2 formatter.decimalSeparator = "." return formatter }() } extension JSONDecoder.DateDecodingStrategy { static let customISO8601 = custom { let container = try $0.singleValueContainer() let string = try container.decode(String.self) if let date = Formatter.iso8601withFractionalSeconds.date(from: string) ?? Formatter.iso8601.date(from: string) { return date } throw DecodingError.dataCorruptedError(in: container, debugDescription: "Invalid date: \(string)") } } extension JSONEncoder.DateEncodingStrategy { static let customISO8601 = custom { var container = $1.singleValueContainer() try container.encode(Formatter.iso8601withFractionalSeconds.string(from: $0)) } }