diff --git a/Package.swift b/Package.swift index b0c50e0..9ba4330 100644 --- a/Package.swift +++ b/Package.swift @@ -5,7 +5,7 @@ import PackageDescription let package = Package( name: "JimUtilitySDK", - platforms: [.iOS("14")], + platforms: [.iOS("15")], products: [ // Products define the executables and libraries produced by a package, and make them visible to other packages. .library( diff --git a/Sources/JimUtilitySDK/AppReviewManager/AppReviewManager.swift b/Sources/JimUtilitySDK/AppReviewManager/AppReviewManager.swift index 41cae66..099fc82 100644 --- a/Sources/JimUtilitySDK/AppReviewManager/AppReviewManager.swift +++ b/Sources/JimUtilitySDK/AppReviewManager/AppReviewManager.swift @@ -9,7 +9,8 @@ import Foundation import StoreKit public class AppReviewManager { - + /// Checks if the app review request should be presented to the user based on the launch count. + /// - Parameter frequencyCount: The number of app launches after which the review request should be presented. The default value is 3. public static func checkAppReview(frequencyCount: Int = 3) { let defaultManager = DefaultManager.shared let launchCount = defaultManager.getUserInteger(key: "AppLaunchCount") diff --git a/Sources/JimUtilitySDK/CastManager/CastManager.swift b/Sources/JimUtilitySDK/CastManager/CastManager.swift index 38005df..fa9bac0 100644 --- a/Sources/JimUtilitySDK/CastManager/CastManager.swift +++ b/Sources/JimUtilitySDK/CastManager/CastManager.swift @@ -8,7 +8,12 @@ import Foundation public class CastManager { - + /// Forces the casting of an object to a specified type. + /// - Parameters: + /// - object: The object to be cast. + /// - type: The type to which the object should be cast. + /// - Returns: The object cast to the specified type. + /// - Note: This method will cause a runtime crash if the object cannot be cast to the specified type. public static func forceCast(_ object: Any, to type: T.Type) -> T { guard let typedObject = object as? T else { fatalError("Expected object: \(object) to be of type: \(type)") diff --git a/Sources/JimUtilitySDK/DateManager/DateManager.swift b/Sources/JimUtilitySDK/DateManager/DateManager.swift index 6aa8b69..a337f54 100644 --- a/Sources/JimUtilitySDK/DateManager/DateManager.swift +++ b/Sources/JimUtilitySDK/DateManager/DateManager.swift @@ -8,23 +8,98 @@ import Foundation public class DateManager { - - /** Convert the date to string **/ - public static func convertToString(_ date: Date, format: String) -> String { + /// Converts a `Date` object to a string using the specified format. + /// - Parameters: + /// - date: The `Date` object to be formatted. + /// - format: The format string used to format the date. + /// - Returns: A string representation of the date in the specified format. + public static func dateString(from date: Date, format: String) -> String { let formatter = DateFormatter() formatter.dateFormat = format return formatter.string(from: date) } - - /** Convert the string to date with current time zone **/ - public static func convertToDate(_ str: String, format: String) -> Date? { + + /// Converts a date string to a `Date` object using the specified format. + /// - Parameters: + /// - dateString: The date string to be converted. + /// - format: The format string used to parse the date. + /// - Returns: A `Date` object if the parsing is successful, otherwise `nil`. + public static func date(from dateString: String, format: String) -> Date? { let formatter = DateFormatter() formatter.dateFormat = format - formatter.timeZone = TimeZone.current - guard let formattedDate = formatter.date(from: str) else { - print("ERROR: The date is nil") + return formatter.date(from: dateString) + } + + /// Converts a `Date` object to an ISO 8601 string. + /// - Parameter date: The `Date` object to be formatted. + /// - Returns: A string representation of the date in ISO 8601 format. + public static func iso8601String(from date: Date) -> String { + let formatter = ISO8601DateFormatter() + return formatter.string(from: date) + } + + /// Converts an ISO 8601 string to a `Date` object. + /// - Parameter dateString: The ISO 8601 string to be converted. + /// - Returns: A `Date` object if the parsing is successful, otherwise `nil`. + public static func date(fromISO8601String dateString: String) -> Date? { + let formatter = ISO8601DateFormatter() + return formatter.date(from: dateString) + } + + /// Converts a date string from one format to another format. + /// - Parameters: + /// - dateString: The date string to be converted. + /// - fromFormat: The format string used to parse the original date string. + /// - toFormat: The format string used to format the resulting date string. + /// - Returns: A string representation of the date in the new format if the parsing is successful, otherwise `nil`. + public static func formattedDateString(from dateString: String, fromFormat: String, toFormat: String) -> String? { + guard let date = DateManager.date(from: dateString, format: fromFormat) else { return nil } - return formattedDate + return DateManager.dateString(from: date, format: toFormat) + } + + // MARK: - Commonly used date formats + + /// Converts a `Date` object to a short date string (e.g., "MM/dd/yyyy"). + /// - Parameter date: The `Date` object to be formatted. + /// - Returns: A string representation of the date in "MM/dd/yyyy" format. + public static func shortDateString(from date: Date) -> String { + return dateString(from: date, format: "MM/dd/yyyy") // Ex: 01/16/2023 + } + + /// Converts a `Date` object to a full date string (e.g., "EEEE, MMM d, yyyy"). + /// - Parameter date: The `Date` object to be formatted. + /// - Returns: A string representation of the date in "EEEE, MMM d, yyyy" format. + public static func fullDateString(from date: Date) -> String { + return dateString(from: date, format: "EEEE, MMM d, yyyy") // Ex: Monday, Jan 16, 2023 + } + + /// Converts a `Date` object to a time string (e.g., "h:mm a"). + /// - Parameter date: The `Date` object to be formatted. + /// - Returns: A string representation of the time in "h:mm a" format. + public static func timeString(from date: Date) -> String { + return dateString(from: date, format: "h:mm a") // Ex: 3:45 PM + } + + /// Converts a `Date` object to a date and time string (e.g., "MM/dd/yyyy h:mm a"). + /// - Parameter date: The `Date` object to be formatted. + /// - Returns: A string representation of the date and time in "MM/dd/yyyy h:mm a" format. + public static func dateTimeString(from date: Date) -> String { + return dateString(from: date, format: "MM/dd/yyyy h:mm a") // Ex: 01/16/2023 3:45 PM + } + + /// Converts a `Date` object to a short time string (e.g., "HH:mm"). + /// - Parameter date: The `Date` object to be formatted. + /// - Returns: A string representation of the time in "HH:mm" format. + public static func shortTimeString(from date: Date) -> String { + return dateString(from: date, format: "HH:mm") // Ex: 15:45 + } + + /// Converts a `Date` object to a month, day, and year string (e.g., "MMM d, yyyy"). + /// - Parameter date: The `Date` object to be formatted. + /// - Returns: A string representation of the date in "MMM d, yyyy" format. + public static func monthDayYearString(from date: Date) -> String { + return dateString(from: date, format: "MMM d, yyyy") // Ex: Jan 16, 2023 } } diff --git a/Sources/JimUtilitySDK/Extensions/Data/PrettyPrintedJSONString.swift b/Sources/JimUtilitySDK/Extensions/Data/PrettyPrintedJSONString.swift new file mode 100644 index 0000000..2730bd9 --- /dev/null +++ b/Sources/JimUtilitySDK/Extensions/Data/PrettyPrintedJSONString.swift @@ -0,0 +1,18 @@ +// +// PrettyPrintedJSONString.swift +// +// +// Created by James Layton on 7/11/24. +// + +import Foundation + +public extension Data { + var prettyPrintedJSONString: NSString? { + guard let object = try? JSONSerialization.jsonObject(with: self, options: []), + let data = try? JSONSerialization.data(withJSONObject: object, options: [.prettyPrinted]), + let prettyPrintedString = NSString(data: data, encoding: String.Encoding.utf8.rawValue) else { return nil } + + return prettyPrintedString + } +} diff --git a/Sources/JimUtilitySDK/Extensions/String/FormattedDecimalNumber.swift b/Sources/JimUtilitySDK/Extensions/String/FormattedDecimalNumber.swift new file mode 100644 index 0000000..9e84d46 --- /dev/null +++ b/Sources/JimUtilitySDK/Extensions/String/FormattedDecimalNumber.swift @@ -0,0 +1,21 @@ +// +// FormattedDecimalNumber.swift +// +// +// Created by James Layton on 7/11/24. +// + +import Foundation + +public extension String { + func FormattedDecimalNumber(_ maximumFractionDigits: Int = 0) -> String? { + guard let number = Double(self) else { return nil } + + let formatter = NumberFormatter() + formatter.numberStyle = .decimal + formatter.maximumFractionDigits = maximumFractionDigits // Optional: Adjust if you want to show decimals + + return formatter.string(from: NSNumber(value: number)) + } +} + diff --git a/Sources/JimUtilitySDK/Extensions/String/String+isValidEmail.swift b/Sources/JimUtilitySDK/Extensions/String/IsValidEmail.swift similarity index 93% rename from Sources/JimUtilitySDK/Extensions/String/String+isValidEmail.swift rename to Sources/JimUtilitySDK/Extensions/String/IsValidEmail.swift index 7b03791..9ccf01e 100644 --- a/Sources/JimUtilitySDK/Extensions/String/String+isValidEmail.swift +++ b/Sources/JimUtilitySDK/Extensions/String/IsValidEmail.swift @@ -1,5 +1,5 @@ // -// String+isValidEmail.swift +// IsValidEmail.swift // // // Created by James Layton on 6/21/20. diff --git a/Sources/JimUtilitySDK/Extensions/String/String+isValidPassword.swift b/Sources/JimUtilitySDK/Extensions/String/IsValidPassword.swift similarity index 98% rename from Sources/JimUtilitySDK/Extensions/String/String+isValidPassword.swift rename to Sources/JimUtilitySDK/Extensions/String/IsValidPassword.swift index b192bf2..bcc1160 100644 --- a/Sources/JimUtilitySDK/Extensions/String/String+isValidPassword.swift +++ b/Sources/JimUtilitySDK/Extensions/String/IsValidPassword.swift @@ -1,5 +1,5 @@ // -// File.swift +// IsValidPassword.swift // // // Created by James Layton on 1/17/23. diff --git a/Sources/JimUtilitySDK/Extensions/View/HideKeyboard.swift b/Sources/JimUtilitySDK/Extensions/View/HideKeyboard.swift new file mode 100644 index 0000000..04f6a90 --- /dev/null +++ b/Sources/JimUtilitySDK/Extensions/View/HideKeyboard.swift @@ -0,0 +1,15 @@ +// +// HideKeyboard.swift +// +// +// Created by James Layton on 7/11/24. +// + +import SwiftUI + +public extension View { + func hideKeyboard() { + let resign = #selector(UIResponder.resignFirstResponder) + UIApplication.shared.sendAction(resign, to: nil, from: nil, for: nil) + } +} diff --git a/Sources/JimUtilitySDK/Extensions/View/NavigationBarColor.swift b/Sources/JimUtilitySDK/Extensions/View/NavigationBarColor.swift new file mode 100644 index 0000000..3d246cf --- /dev/null +++ b/Sources/JimUtilitySDK/Extensions/View/NavigationBarColor.swift @@ -0,0 +1,32 @@ +// +// NavigationBarColor.swift +// +// +// Created by James Layton on 7/11/24. +// + +import SwiftUI + +struct NavigationBarColorModifier: ViewModifier { + var backgroundColor: UIColor? + + init(backgroundColor: UIColor?) { + self.backgroundColor = backgroundColor + let appearance = UINavigationBarAppearance() + appearance.backgroundColor = backgroundColor + appearance.titleTextAttributes = [.foregroundColor: UIColor.white] + appearance.largeTitleTextAttributes = [.foregroundColor: UIColor.white] + UINavigationBar.appearance().standardAppearance = appearance + UINavigationBar.appearance().scrollEdgeAppearance = appearance + } + + func body(content: Content) -> some View { + content + } +} + +public extension View { + func navigationBarColor(_ backgroundColor: UIColor?) -> some View { + self.modifier(NavigationBarColorModifier(backgroundColor: backgroundColor)) + } +} diff --git a/Sources/JimUtilitySDK/Extensions/View/OnFirstAppearOrTask.swift b/Sources/JimUtilitySDK/Extensions/View/OnFirstAppearOrTask.swift new file mode 100644 index 0000000..7d62c40 --- /dev/null +++ b/Sources/JimUtilitySDK/Extensions/View/OnFirstAppearOrTask.swift @@ -0,0 +1,53 @@ +// +// OnFirstAppearOrTask.swift +// +// +// Created by James Layton on 7/11/24. +// + +import SwiftUI + +public extension View { + func onFirstAppear(_ action: @escaping () -> Void) -> some View { + modifier(FirstAppear(action: action)) + } + + func onFirstTask(_ action: @escaping () async -> Void) -> some View { + modifier(FirstTask(action: action)) + } +} + +private struct FirstAppear: ViewModifier { + let action: () -> Void + + // Use this to only fire your block one time + @State private var hasAppeared = false + + func body(content: Content) -> some View { + // And then, track it here + content.onAppear { + guard !hasAppeared else { return } + hasAppeared = true + action() + } + } +} + +private struct FirstTask: ViewModifier { + let action: () async -> Void + + // Use this to only fire your block one time + @State private var hasAppeared = false + + func body(content: Content) -> some View { + // And then, track it here + content.onAppear { + guard !hasAppeared else { return } + hasAppeared = true + + Task { + await action() + } + } + } +}