diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6b64884..530072f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,9 +21,9 @@ jobs: strategy: matrix: destination: - - platform=iOS Simulator,name=iPhone 16,OS=18.1 - - platform=watchOS Simulator,name=Apple Watch SE (40mm) (2nd generation),OS=11.1 - - platform=tvOS Simulator,name=Apple TV 4K (3rd generation) (at 1080p),OS=18.1 + - platform=iOS Simulator,name=iPhone 17,OS=26.2 + - platform=watchOS Simulator,name=Apple Watch SE 3 (40mm),OS=26.2 + - platform=tvOS Simulator,name=Apple TV 4K (3rd generation) (at 1080p),OS=26.2 - platform=macOS,arch=arm64 with: package: "OversizeCore" diff --git a/Package.swift b/Package.swift index cce0368..d843ccd 100644 --- a/Package.swift +++ b/Package.swift @@ -16,8 +16,6 @@ let package = Package( name: "OversizeCore", targets: ["OversizeCore"], ), ], - dependencies: [ - ], targets: [ .target( name: "OversizeCore", diff --git a/Sources/OversizeCore/Error/CalendarError.swift b/Sources/OversizeCore/Error/CalendarError.swift new file mode 100644 index 0000000..8db7b8c --- /dev/null +++ b/Sources/OversizeCore/Error/CalendarError.swift @@ -0,0 +1,86 @@ +// +// Copyright © 2025 Alexander Romanov +// CalendarError.swift, created on 02.02.2025 +// + +import Foundation + +public enum CalendarError: Error, LocalizedError, Sendable { + // MARK: - Core Operations + + case saveFailed + case fetchFailed + case deleteFailed + case updateFailed + + // MARK: - Permission + + case accessDenied + + // MARK: - Data + + case itemNotFound + + // MARK: - Unknown + + case unknown(Error?) + + // MARK: - LocalizedError + + public var errorDescription: String? { + switch self { + case .saveFailed: + "Failed to save event" + case .fetchFailed: + "Failed to fetch events" + case .deleteFailed: + "Failed to delete event" + case .updateFailed: + "Failed to update event" + case .accessDenied: + "No access to calendar" + case .itemNotFound: + "Event not found" + case .unknown: + "Calendar error" + } + } + + public var failureReason: String? { + switch self { + case .saveFailed: + "The event could not be saved to the calendar." + case .fetchFailed: + "The events could not be retrieved from the calendar." + case .deleteFailed: + "The event could not be deleted from the calendar." + case .updateFailed: + "The event could not be updated in the calendar." + case .accessDenied: + "Calendar access is restricted or denied." + case .itemNotFound: + "The requested event could not be found." + case let .unknown(error): + error?.localizedDescription ?? "An unknown calendar error occurred." + } + } + + public var recoverySuggestion: String? { + switch self { + case .saveFailed: + "Check if the calendar is writable and try again." + case .fetchFailed: + "Refresh the calendar and try again." + case .deleteFailed: + "Make sure the event exists and try again." + case .updateFailed: + "Make sure the event exists and try again." + case .accessDenied: + "Allow calendar access in Settings and try again." + case .itemNotFound: + "Refresh the calendar and try again." + case .unknown: + "Please try again later." + } + } +} diff --git a/Sources/OversizeCore/Error/CloudError.swift b/Sources/OversizeCore/Error/CloudError.swift new file mode 100644 index 0000000..fa751b9 --- /dev/null +++ b/Sources/OversizeCore/Error/CloudError.swift @@ -0,0 +1,110 @@ +// +// Copyright © 2025 Alexander Romanov +// CloudError.swift, created on 02.02.2025 +// + +import Foundation + +public enum CloudError: Error, LocalizedError, Sendable { + // MARK: - Core Operations + + case saveFailed + case fetchFailed + case deleteFailed + case updateFailed + + // MARK: - Account & Permission + + case accessDenied + case noAccount + + // MARK: - Network & Storage + + case networkUnavailable + case quotaExceeded + + // MARK: - Data + + case decode + + // MARK: - Unknown + + case unknown(Error?) + + // MARK: - LocalizedError + + public var errorDescription: String? { + switch self { + case .saveFailed: + "Failed to save to iCloud" + case .fetchFailed: + "Failed to fetch from iCloud" + case .deleteFailed: + "Failed to delete from iCloud" + case .updateFailed: + "Failed to update in iCloud" + case .accessDenied: + "No access to iCloud" + case .noAccount: + "No iCloud account" + case .networkUnavailable: + "iCloud network unavailable" + case .quotaExceeded: + "iCloud storage full" + case .decode: + "iCloud data could not be read" + case .unknown: + "iCloud error" + } + } + + public var failureReason: String? { + switch self { + case .saveFailed: + "The data could not be saved to iCloud." + case .fetchFailed: + "The data could not be retrieved from iCloud." + case .deleteFailed: + "The data could not be deleted from iCloud." + case .updateFailed: + "The data could not be updated in iCloud." + case .accessDenied: + "iCloud access is restricted or denied." + case .noAccount: + "An iCloud account is required." + case .networkUnavailable: + "The network connection to iCloud is unavailable." + case .quotaExceeded: + "Your iCloud storage quota has been exceeded." + case .decode: + "The iCloud data format was not recognized." + case let .unknown(error): + error?.localizedDescription ?? "An unknown iCloud error occurred." + } + } + + public var recoverySuggestion: String? { + switch self { + case .saveFailed: + "Check your internet connection and iCloud storage, then try again." + case .fetchFailed: + "Check your internet connection and try again." + case .deleteFailed: + "Make sure the item exists and try again." + case .updateFailed: + "Make sure the item exists and try again." + case .accessDenied: + "Sign in to iCloud in Settings and try again." + case .noAccount: + "Sign in to iCloud in Settings and try again." + case .networkUnavailable: + "Check your internet connection and try again." + case .quotaExceeded: + "Free up iCloud storage or upgrade your plan." + case .decode: + "Try refreshing the data or reinstalling the app." + case .unknown: + "Please try again later." + } + } +} diff --git a/Sources/OversizeCore/Error/ContactsError.swift b/Sources/OversizeCore/Error/ContactsError.swift new file mode 100644 index 0000000..931bf4f --- /dev/null +++ b/Sources/OversizeCore/Error/ContactsError.swift @@ -0,0 +1,83 @@ +// +// Copyright © 2025 Alexander Romanov +// ContactsError.swift, created on 01.02.2025 +// + +import Foundation + +public enum ContactsError: Error, LocalizedError, Sendable { + // MARK: - Core Operations + + case saveFailed + case fetchFailed + case deleteFailed + case updateFailed + + // MARK: - Permission + + case accessDenied + + // MARK: - Unknown + + case unknown(Error?) + + // MARK: - LocalizedError + + public var errorDescription: String? { + switch self { + case .saveFailed: + "Failed to save contact" + case .fetchFailed: + "Failed to fetch contacts" + case .deleteFailed: + "Failed to delete contact" + case .updateFailed: + "Failed to update contact" + case .accessDenied: + "No access to contacts" + case .unknown: + "Contacts error" + } + } + + public var failureReason: String? { + switch self { + case .saveFailed: + "The contact could not be saved to the address book." + case .fetchFailed: + "The contacts could not be retrieved from the address book." + case .deleteFailed: + "The contact could not be deleted from the address book." + case .updateFailed: + "The contact could not be updated in the address book." + case .accessDenied: + "Contacts access is restricted or denied." + case let .unknown(error): + error?.localizedDescription ?? "An unknown contacts error occurred." + } + } + + public var recoverySuggestion: String? { + switch self { + case .saveFailed: + "Check if the contact data is valid and try again." + case .fetchFailed: + "Refresh the contacts and try again." + case .deleteFailed: + "Make sure the contact exists and try again." + case .updateFailed: + "Make sure the contact exists and try again." + case .accessDenied: + "Allow contacts access in Settings and try again." + case .unknown: + "Please try again later." + } + } + + // MARK: - Deprecated Aliases + + @available(*, deprecated, renamed: "accessDenied") + public static var notAccess: ContactsError { + .accessDenied + } +} diff --git a/Sources/OversizeCore/Error/CustomError.swift b/Sources/OversizeCore/Error/CustomError.swift new file mode 100644 index 0000000..9c3f48c --- /dev/null +++ b/Sources/OversizeCore/Error/CustomError.swift @@ -0,0 +1,30 @@ +// +// Copyright © 2025 Alexander Romanov +// CustomError.swift, created on 01.02.2025 +// + +import Foundation + +public struct CustomError: Error, LocalizedError, Sendable { + public let title: String + public let detail: String? + public let suggestion: String? + + public init(title: String, detail: String? = nil, suggestion: String? = nil) { + self.title = title + self.detail = detail + self.suggestion = suggestion + } + + public var errorDescription: String? { + title + } + + public var failureReason: String? { + detail + } + + public var recoverySuggestion: String? { + suggestion + } +} diff --git a/Sources/OversizeCore/Error/Deprecated.swift b/Sources/OversizeCore/Error/Deprecated.swift new file mode 100644 index 0000000..b45339f --- /dev/null +++ b/Sources/OversizeCore/Error/Deprecated.swift @@ -0,0 +1,62 @@ +// +// Copyright © 2025 Alexander Romanov +// Deprecated.swift, created on 02.02.2025 +// + +import Foundation + +// MARK: - Deprecated Type Aliases + +@available(*, deprecated, renamed: "PersistenceError") +public typealias CoreDataError = PersistenceError + +@available(*, deprecated, renamed: "PersistenceError") +public typealias SwiftDataError = PersistenceError + +@available(*, deprecated, renamed: "FileError") +public typealias FileManagerError = FileError + +@available(*, deprecated, renamed: "CloudError") +public typealias CloudDocumentsError = CloudError + +@available(*, deprecated, renamed: "CloudError") +public typealias CloudKitError = CloudError + +@available(*, deprecated, renamed: "CalendarError") +public typealias EventKitError = CalendarError + +@available(*, deprecated, renamed: "HealthError") +public typealias HealthKitError = HealthError + +@available(*, deprecated, message: "Use FileError or CloudError instead") +public enum FileSyncError: Error, LocalizedError, Sendable { + case file(FileError) + case cloud(CloudError) + + public var errorDescription: String? { + switch self { + case let .file(error): + error.errorDescription + case let .cloud(error): + error.errorDescription + } + } + + public var failureReason: String? { + switch self { + case let .file(error): + error.failureReason + case let .cloud(error): + error.failureReason + } + } + + public var recoverySuggestion: String? { + switch self { + case let .file(error): + error.recoverySuggestion + case let .cloud(error): + error.recoverySuggestion + } + } +} diff --git a/Sources/OversizeCore/Error/FileError.swift b/Sources/OversizeCore/Error/FileError.swift new file mode 100644 index 0000000..cff15ea --- /dev/null +++ b/Sources/OversizeCore/Error/FileError.swift @@ -0,0 +1,76 @@ +// +// Copyright © 2025 Alexander Romanov +// FileError.swift, created on 02.02.2025 +// + +import Foundation + +public enum FileError: Error, LocalizedError, Sendable { + // MARK: - Core Operations + + case saveFailed + case fetchFailed + case deleteFailed + case updateFailed + + // MARK: - Permission + + case accessDenied + + // MARK: - Unknown + + case unknown(Error?) + + // MARK: - LocalizedError + + public var errorDescription: String? { + switch self { + case .saveFailed: + "Failed to save file" + case .fetchFailed: + "Failed to fetch files" + case .deleteFailed: + "Failed to delete file" + case .updateFailed: + "Failed to update file" + case .accessDenied: + "No access to files" + case .unknown: + "File error" + } + } + + public var failureReason: String? { + switch self { + case .saveFailed: + "The file could not be saved to disk." + case .fetchFailed: + "The files could not be retrieved from disk." + case .deleteFailed: + "The file could not be deleted from disk." + case .updateFailed: + "The file could not be updated on disk." + case .accessDenied: + "File access is restricted or denied." + case let .unknown(error): + error?.localizedDescription ?? "An unknown file error occurred." + } + } + + public var recoverySuggestion: String? { + switch self { + case .saveFailed: + "Check if there is enough storage space and try again." + case .fetchFailed: + "Make sure the file exists and try again." + case .deleteFailed: + "Make sure the file exists and you have permission to delete it." + case .updateFailed: + "Make sure the file exists and you have permission to modify it." + case .accessDenied: + "Allow file access in Settings and try again." + case .unknown: + "Please try again later." + } + } +} diff --git a/Sources/OversizeCore/Error/HealthError.swift b/Sources/OversizeCore/Error/HealthError.swift new file mode 100644 index 0000000..c902080 --- /dev/null +++ b/Sources/OversizeCore/Error/HealthError.swift @@ -0,0 +1,93 @@ +// +// Copyright © 2025 Alexander Romanov +// HealthError.swift, created on 02.02.2025 +// + +import Foundation + +public enum HealthError: Error, LocalizedError, Sendable { + // MARK: - Core Operations + + case saveFailed + case fetchFailed + case deleteFailed + case updateFailed + + // MARK: - Permission + + case accessDenied + case authorizationNotDetermined + + // MARK: - Data + + case dataTypeNotAvailable + + // MARK: - Unknown + + case unknown(Error?) + + // MARK: - LocalizedError + + public var errorDescription: String? { + switch self { + case .saveFailed: + "Failed to save Health data" + case .fetchFailed: + "Failed to fetch Health data" + case .deleteFailed: + "Failed to delete Health data" + case .updateFailed: + "Failed to update Health data" + case .accessDenied: + "No access to Health data" + case .authorizationNotDetermined: + "Health authorization not determined" + case .dataTypeNotAvailable: + "Health data type not available" + case .unknown: + "Health error" + } + } + + public var failureReason: String? { + switch self { + case .saveFailed: + "The data could not be saved to Health." + case .fetchFailed: + "The data could not be retrieved from Health." + case .deleteFailed: + "The data could not be deleted from Health." + case .updateFailed: + "The data could not be updated in Health." + case .accessDenied: + "Health access is restricted or denied." + case .authorizationNotDetermined: + "Health authorization has not been granted yet." + case .dataTypeNotAvailable: + "The requested health data type is not available on this device." + case let .unknown(error): + error?.localizedDescription ?? "An unknown Health error occurred." + } + } + + public var recoverySuggestion: String? { + switch self { + case .saveFailed: + "Check Health permissions and try again." + case .fetchFailed: + "Check Health permissions and try again." + case .deleteFailed: + "Make sure the data exists and try again." + case .updateFailed: + "Make sure the data exists and try again." + case .accessDenied: + "Allow Health access in Settings and try again." + case .authorizationNotDetermined: + "Allow Health access when prompted and try again." + case .dataTypeNotAvailable: + "This feature requires a device that supports this health data type." + case .unknown: + "Please try again later." + } + } +} diff --git a/Sources/OversizeCore/Error/LocationError.swift b/Sources/OversizeCore/Error/LocationError.swift new file mode 100644 index 0000000..6865584 --- /dev/null +++ b/Sources/OversizeCore/Error/LocationError.swift @@ -0,0 +1,81 @@ +// +// Copyright © 2025 Alexander Romanov +// LocationError.swift, created on 01.02.2025 +// + +import Foundation + +public enum LocationError: Error, LocalizedError, Sendable { + // MARK: - Permission + + case permissionNotDetermined + case accessDenied + + // MARK: - Location + + case locationUnavailable + case locationTimeout + + // MARK: - Unknown + + case unknown(Error?) + + // MARK: - LocalizedError + + public var errorDescription: String? { + switch self { + case .permissionNotDetermined: + "Location access not determined" + case .accessDenied: + "No access to location" + case .locationUnavailable: + "Location unavailable" + case .locationTimeout: + "Location request timed out" + case .unknown: + "Location error" + } + } + + public var failureReason: String? { + switch self { + case .permissionNotDetermined: + "Location permission has not been granted yet." + case .accessDenied: + "Location access is restricted or denied." + case .locationUnavailable: + "The device could not determine its location." + case .locationTimeout: + "The location request took too long to complete." + case let .unknown(error): + error?.localizedDescription ?? "An unknown location error occurred." + } + } + + public var recoverySuggestion: String? { + switch self { + case .permissionNotDetermined: + "Allow location access when prompted and try again." + case .accessDenied: + "Allow location access in Settings and try again." + case .locationUnavailable: + "Make sure location services are enabled and you have a clear view of the sky." + case .locationTimeout: + "Move to an area with better GPS reception and try again." + case .unknown: + "Please try again later." + } + } + + // MARK: - Deprecated Aliases + + @available(*, deprecated, renamed: "permissionNotDetermined") + public static var notDetermined: LocationError { + .permissionNotDetermined + } + + @available(*, deprecated, renamed: "accessDenied") + public static var notAccess: LocationError { + .accessDenied + } +} diff --git a/Sources/OversizeCore/Error/NetworkError.swift b/Sources/OversizeCore/Error/NetworkError.swift new file mode 100644 index 0000000..5240f16 --- /dev/null +++ b/Sources/OversizeCore/Error/NetworkError.swift @@ -0,0 +1,103 @@ +// +// Copyright © 2025 Alexander Romanov +// NetworkError.swift, created on 01.02.2025 +// + +import Foundation + +public enum NetworkError: Error, LocalizedError, Sendable { + // MARK: - Request + + case invalidURL + case timeout + case noConnection + + // MARK: - Response + + case decode + case noResponse + case unexpectedStatusCode + + // MARK: - Authentication + + case unauthorized + + // MARK: - API + + case apiError(title: String, detail: String?) + + // MARK: - Unknown + + case unknown(Error?) + + // MARK: - LocalizedError + + public var errorDescription: String? { + switch self { + case .invalidURL: + "Invalid request URL" + case .timeout: + "Request timed out" + case .noConnection: + "No internet connection" + case .decode: + "Failed to decode server response" + case .noResponse: + "No response from server" + case .unexpectedStatusCode: + "Unexpected status code" + case .unauthorized: + "Unauthorized" + case let .apiError(title, _): + title + case .unknown: + "Unknown network error" + } + } + + public var failureReason: String? { + switch self { + case .invalidURL: + "The request URL is malformed." + case .timeout: + "The request took too long to complete." + case .noConnection: + "The device is not connected to the internet." + case .decode: + "The response format was not recognized." + case .noResponse: + "The server did not return a response." + case .unexpectedStatusCode: + "The server returned an unexpected status code." + case .unauthorized: + "Authentication is required." + case let .apiError(_, detail): + detail + case let .unknown(error): + error?.localizedDescription ?? "The request failed for an unknown reason." + } + } + + public var recoverySuggestion: String? { + switch self { + case .invalidURL: + "Check the request parameters and try again." + case .timeout: + "Check your internet connection and try again." + case .noConnection: + "Connect to the internet and try again." + case .decode: + "Try again later or contact support." + case .noResponse: + "Check your internet connection and try again." + case .unexpectedStatusCode: + "Try again later or contact support." + case .unauthorized: + "Sign in and try again." + case .apiError: + "Please try again later." + case .unknown: + "Please try again later." + } + } +} diff --git a/Sources/OversizeCore/Error/NotificationError.swift b/Sources/OversizeCore/Error/NotificationError.swift new file mode 100644 index 0000000..d6658a3 --- /dev/null +++ b/Sources/OversizeCore/Error/NotificationError.swift @@ -0,0 +1,74 @@ +// +// Copyright © 2025 Alexander Romanov +// NotificationError.swift, created on 01.02.2025 +// + +import Foundation + +public enum NotificationError: Error, LocalizedError, Sendable { + // MARK: - Permission + + case permissionNotDetermined + case accessDenied + + // MARK: - Operations + + case schedulingFailed + + // MARK: - Unknown + + case unknown(Error?) + + // MARK: - LocalizedError + + public var errorDescription: String? { + switch self { + case .permissionNotDetermined: + "Notification permission not determined" + case .accessDenied: + "No access to notifications" + case .schedulingFailed: + "Failed to schedule notification" + case .unknown: + "Notification error" + } + } + + public var failureReason: String? { + switch self { + case .permissionNotDetermined: + "Notification permission has not been granted yet." + case .accessDenied: + "Notification access is restricted or denied." + case .schedulingFailed: + "The notification could not be scheduled." + case let .unknown(error): + error?.localizedDescription ?? "An unknown notification error occurred." + } + } + + public var recoverySuggestion: String? { + switch self { + case .permissionNotDetermined: + "Allow notifications when prompted and try again." + case .accessDenied: + "Allow notifications in Settings and try again." + case .schedulingFailed: + "Check the notification settings and try again." + case .unknown: + "Please try again later." + } + } + + // MARK: - Deprecated Aliases + + @available(*, deprecated, renamed: "permissionNotDetermined") + public static var notDetermined: NotificationError { + .permissionNotDetermined + } + + @available(*, deprecated, renamed: "accessDenied") + public static var notAccess: NotificationError { + .accessDenied + } +} diff --git a/Sources/OversizeCore/Error/PersistenceError.swift b/Sources/OversizeCore/Error/PersistenceError.swift new file mode 100644 index 0000000..7a167f5 --- /dev/null +++ b/Sources/OversizeCore/Error/PersistenceError.swift @@ -0,0 +1,199 @@ +// +// Copyright © 2025 Alexander Romanov +// PersistenceError.swift, created on 02.02.2025 +// + +import Foundation + +public enum PersistenceError: Error, LocalizedError, Sendable { + // MARK: - Core Operations + + case saveFailed + case fetchFailed + case deleteFailed + case updateFailed + + // MARK: - Batch Operations + + case batchOperationFailed + + // MARK: - Model & Configuration + + case migrationFailed + case modelConfigurationError + case containerInitializationFailed + + // MARK: - Query & Predicate + + case invalidPredicate + case invalidSortDescriptor + case invalidFetchDescriptor + + // MARK: - Data Integrity + + case duplicateItem + case itemNotFound + case relationshipConstraintViolation + case validationFailed(reason: String) + + // MARK: - Transaction + + case transactionFailed + case concurrentModification + + // MARK: - Performance + + case indexingError + + // MARK: - Storage + + case storageLimitExceeded + case corruptedData + + // MARK: - Unknown + + case unknown(Error?) + + // MARK: - LocalizedError + + public var errorDescription: String? { + switch self { + case .saveFailed: + "Failed to save data" + case .fetchFailed: + "Failed to fetch data" + case .deleteFailed: + "Failed to delete data" + case .updateFailed: + "Failed to update data" + case .batchOperationFailed: + "Batch operation failed" + case .migrationFailed: + "Data migration failed" + case .modelConfigurationError: + "Model configuration error" + case .containerInitializationFailed: + "Failed to initialize data container" + case .invalidPredicate: + "Invalid search criteria" + case .invalidSortDescriptor: + "Invalid sort order" + case .invalidFetchDescriptor: + "Invalid fetch configuration" + case .duplicateItem: + "Item already exists" + case .itemNotFound: + "Item not found" + case .relationshipConstraintViolation: + "Relationship constraint violated" + case .validationFailed: + "Data validation failed" + case .transactionFailed: + "Transaction failed" + case .concurrentModification: + "Concurrent modification detected" + case .indexingError: + "Indexing error" + case .storageLimitExceeded: + "Storage limit exceeded" + case .corruptedData: + "Data is corrupted" + case .unknown: + "Data persistence error" + } + } + + public var failureReason: String? { + switch self { + case .saveFailed: + "The data could not be saved to the database." + case .fetchFailed: + "The data could not be retrieved from the database." + case .deleteFailed: + "The data could not be deleted from the database." + case .updateFailed: + "The data could not be updated in the database." + case .batchOperationFailed: + "The batch operation could not be completed." + case .migrationFailed: + "The data migration could not be completed." + case .modelConfigurationError: + "The data model configuration is invalid." + case .containerInitializationFailed: + "The data container could not be initialized." + case .invalidPredicate: + "The search criteria format is not valid." + case .invalidSortDescriptor: + "The sort order specification is not valid." + case .invalidFetchDescriptor: + "The fetch configuration is not valid." + case .duplicateItem: + "An item with the same identifier already exists." + case .itemNotFound: + "The requested item does not exist in the database." + case .relationshipConstraintViolation: + "The operation violates a relationship constraint." + case let .validationFailed(reason): + reason + case .transactionFailed: + "The database transaction could not be completed." + case .concurrentModification: + "The data was modified by another process." + case .indexingError: + "The database index could not be updated." + case .storageLimitExceeded: + "The database has reached its storage limit." + case .corruptedData: + "The database contains corrupted data." + case let .unknown(error): + error?.localizedDescription ?? "An unknown data persistence error occurred." + } + } + + public var recoverySuggestion: String? { + switch self { + case .saveFailed: + "Check if there is enough storage space and try again." + case .fetchFailed: + "Refresh the data and try again." + case .deleteFailed: + "Make sure the item exists and try again." + case .updateFailed: + "Make sure the item exists and try again." + case .batchOperationFailed: + "Try performing the operation in smaller batches." + case .migrationFailed: + "The app may need to be reinstalled." + case .modelConfigurationError: + "Restart the app and try again." + case .containerInitializationFailed: + "Restart the app and try again." + case .invalidPredicate: + "Check your query parameters and try again." + case .invalidSortDescriptor: + "Check your query parameters and try again." + case .invalidFetchDescriptor: + "Check your query parameters and try again." + case .duplicateItem: + "Try updating the existing item instead." + case .itemNotFound: + "Refresh and try again." + case .relationshipConstraintViolation: + "Check the related data and try again." + case .validationFailed: + "Check the data and try again." + case .transactionFailed: + "Please try again later." + case .concurrentModification: + "Reload the data and try again." + case .indexingError: + "Please try again later." + case .storageLimitExceeded: + "Delete some data to free up space." + case .corruptedData: + "The database may need to be reset." + case .unknown: + "Please try again later." + } + } +} diff --git a/Sources/OversizeCore/Extensions/Color/Color+Extension.swift b/Sources/OversizeCore/Extensions/Color/Color+Extension.swift index 8ba275e..55c762a 100644 --- a/Sources/OversizeCore/Extensions/Color/Color+Extension.swift +++ b/Sources/OversizeCore/Extensions/Color/Color+Extension.swift @@ -12,12 +12,12 @@ import SwiftUI public extension Color { /// Extracts the RGBA components of the color. - /// + /// /// This computed property breaks down the color into its red, green, blue, and opacity /// components, providing access to the individual color values for calculations and manipulations. - /// + /// /// - Returns: A tuple containing (red, green, blue, opacity) as CGFloat values from 0.0 to 1.0 - /// + /// /// Example: /// ```swift /// let color = Color.red @@ -25,7 +25,7 @@ public extension Color { /// print("Red: \(components.red), Green: \(components.green)") /// // Output: Red: 1.0, Green: 0.0 /// ``` - /// + /// /// - Note: This property is only available on iOS. Returns (0, 0, 0, 0) if component extraction fails. var components: (red: CGFloat, green: CGFloat, blue: CGFloat, opacity: CGFloat) { #if canImport(UIKit) @@ -46,60 +46,60 @@ public extension Color { } /// Creates a lighter version of the color by the specified percentage. - /// + /// /// This function increases the brightness of the color by adding the specified /// percentage to each RGB component, creating a lighter variant of the original color. - /// + /// /// - Parameter percentage: The percentage to lighten the color (default: 30.0) /// - Returns: A new Color instance that is lighter than the original - /// + /// /// Example: /// ```swift /// let originalColor = Color.blue /// let lighterBlue = originalColor.lighter(by: 20.0) /// let muchLighter = originalColor.lighter() // Uses default 30% /// ``` - /// + /// /// - Note: This function is only available on iOS. Values are clamped to prevent overflow. func lighter(by percentage: CGFloat = 30.0) -> Color { adjust(by: abs(percentage)) } /// Creates a darker version of the color by the specified percentage. - /// + /// /// This function decreases the brightness of the color by subtracting the specified /// percentage from each RGB component, creating a darker variant of the original color. - /// + /// /// - Parameter percentage: The percentage to darken the color (default: 30.0) /// - Returns: A new Color instance that is darker than the original - /// + /// /// Example: /// ```swift /// let originalColor = Color.yellow /// let darkerYellow = originalColor.darker(by: 25.0) /// let muchDarker = originalColor.darker() // Uses default 30% /// ``` - /// + /// /// - Note: This function is only available on iOS. Values are clamped to prevent underflow. func darker(by percentage: CGFloat = 30.0) -> Color { adjust(by: -1 * abs(percentage)) } /// Adjusts the color brightness by the specified percentage. - /// + /// /// This function modifies the RGB components of the color by adding or subtracting /// the specified percentage. Positive values lighten the color, negative values darken it. - /// + /// /// - Parameter percentage: The percentage to adjust (positive for lighter, negative for darker) /// - Returns: A new Color instance with adjusted brightness - /// + /// /// Example: /// ```swift /// let baseColor = Color.green /// let lighter = baseColor.adjust(by: 40.0) // Lighter /// let darker = baseColor.adjust(by: -20.0) // Darker /// ``` - /// + /// /// - Note: This function is only available on iOS. RGB values are clamped between 0.0 and 1.0. func adjust(by percentage: CGFloat = 30.0) -> Color { Color(red: min(Double(components.red + percentage / 100), 1.0), @@ -114,25 +114,25 @@ public extension Color { public extension Color { /// Generates a random color with optional random opacity. - /// + /// /// This static function creates a Color with randomly generated RGB values. /// The opacity can be either fixed at 1.0 (opaque) or randomized as well. /// Useful for testing, prototyping, or creating dynamic color schemes. - /// + /// /// - Parameter randomOpacity: Whether to randomize the opacity as well (default: false) /// - Returns: A Color with random RGB values and either fixed or random opacity - /// + /// /// Example: /// ```swift /// let opaqueRandom = Color.random() // Random color, fully opaque /// let transparentRandom = Color.random(randomOpacity: true) // Random color with random opacity - /// + /// /// // Use in SwiftUI /// Rectangle() /// .fill(Color.random()) /// .frame(width: 100, height: 100) /// ``` - /// + /// /// - Note: All color components (red, green, blue, and optionally opacity) are generated using uniform distribution from 0.0 to 1.0. static func random(randomOpacity: Bool = false) -> Color { Color( diff --git a/Sources/OversizeCore/Extensions/Color/Color+Hex.swift b/Sources/OversizeCore/Extensions/Color/Color+Hex.swift index fa4e697..059d7ee 100644 --- a/Sources/OversizeCore/Extensions/Color/Color+Hex.swift +++ b/Sources/OversizeCore/Extensions/Color/Color+Hex.swift @@ -11,7 +11,7 @@ import AppKit #endif /// A type alias representing the RGBA color components as a tuple. -/// +/// /// This type provides a convenient way to work with color components, /// containing red, green, blue, and alpha values as CGFloat from 0.0 to 1.0. public typealias ColorComponentsRGBA = (red: CGFloat, green: CGFloat, blue: CGFloat, alpha: CGFloat) @@ -22,17 +22,17 @@ public typealias ColorComponentsRGBA = (red: CGFloat, green: CGFloat, blue: CGFl public extension Color { /// Creates a Color from a hexadecimal string representation. - /// + /// /// This initializer supports multiple hex color formats: /// - 3-character RGB (e.g., "F0A" becomes "FF00AA") /// - 6-character RGB (e.g., "FF00AA") /// - 8-character ARGB (e.g., "80FF00AA" with alpha) - /// + /// /// The initializer automatically strips non-alphanumeric characters, /// so formats like "#FF00AA", "0xFF00AA", or "FF-00-AA" are all valid. - /// + /// /// - Parameter hex: The hexadecimal color string - /// + /// /// Example: /// ```swift /// let red = Color(hex: "FF0000") // Pure red @@ -40,7 +40,7 @@ public extension Color { /// let green = Color(hex: "0F0") // Short form green /// let transparent = Color(hex: "80FF0000") // Semi-transparent red /// ``` - /// + /// /// - Note: Invalid hex strings default to black with zero opacity. init(hex: String) { let hex = hex.trimmingCharacters(in: CharacterSet.alphanumerics.inverted) @@ -68,18 +68,18 @@ public extension Color { } /// Creates a Color from an optional hexadecimal string representation. - /// + /// /// This initializer handles optional hex strings safely, providing the same /// functionality as the non-optional version but with nil safety. /// If the hex string is nil, it creates a black color. - /// + /// /// - Parameter hex: The optional hexadecimal color string - /// + /// /// Example: /// ```swift /// let userColor: String? = "#FF0000" /// let safeColor = Color(hex: userColor) // Red color - /// + /// /// let nilColor: String? = nil /// let defaultColor = Color(hex: nilColor) // Black color /// ``` @@ -117,18 +117,18 @@ public extension Color { public extension Color { /// Converts the color to its hexadecimal string representation. - /// + /// /// This function generates a hex string from the color, automatically choosing /// between 6-character RGB format (when alpha is 1.0) or 8-character RGBA format /// (when alpha is not 1.0). - /// + /// /// - Returns: A hex string representation of the color with # prefix - /// + /// /// Example: /// ```swift /// let red = Color.red /// let hex = red.hexString() // "#FF0000" - /// + /// /// let transparentBlue = Color.blue.opacity(0.5) /// let hexWithAlpha = transparentBlue.hexString() // "#0000FF80" /// ``` @@ -137,12 +137,12 @@ public extension Color { } /// Extracts the RGBA components of the color as a tuple. - /// + /// /// This computed property breaks down the color into its red, green, blue, and alpha /// components using the platform's native color representation (UIColor or NSColor). - /// + /// /// - Returns: A ColorComponentsRGBA tuple with values from 0.0 to 1.0 - /// + /// /// Example: /// ```swift /// let purple = Color.purple @@ -165,14 +165,14 @@ public extension Color { // MARK: - Hex Conversion Utilities /// Converts color components to a hexadecimal string representation. -/// +/// /// This function creates a hex string from RGBA color components, automatically /// choosing the appropriate format based on the alpha value. If alpha is 1.0, /// it returns a 6-character RGB hex string, otherwise an 8-character RGBA hex string. -/// +/// /// - Parameter components: The RGBA color components tuple /// - Returns: A hex string with # prefix in RGB or RGBA format -/// +/// /// Example: /// ```swift /// let components: ColorComponentsRGBA = (red: 1.0, green: 0.0, blue: 0.0, alpha: 1.0) diff --git a/Sources/OversizeCore/Extensions/Locale/Currency+Extension.swift b/Sources/OversizeCore/Extensions/Locale/Currency+Extension.swift index e06f2f1..b62b656 100644 --- a/Sources/OversizeCore/Extensions/Locale/Currency+Extension.swift +++ b/Sources/OversizeCore/Extensions/Locale/Currency+Extension.swift @@ -25,6 +25,7 @@ public extension Locale.Currency { } // MARK: - Deprecated aliases for backward compatibility + @available(*, deprecated, renamed: "displayName", message: "Use displayName instead") var dispalyName: String? { displayName diff --git a/Sources/OversizeCore/Extensions/Result/EmptyableLoadingState.swift b/Sources/OversizeCore/Extensions/Result/EmptyableLoadingState.swift new file mode 100644 index 0000000..17ccba3 --- /dev/null +++ b/Sources/OversizeCore/Extensions/Result/EmptyableLoadingState.swift @@ -0,0 +1,89 @@ +// +// Copyright © 2025 Alexander Romanov +// EmptyableLoadingState.swift, created on 02.08.2025 +// + +import Foundation + +public enum EmptyableLoadingState: Sendable { + case idle + case loading + case result(Result) + case empty + case error(Error) +} + +public extension EmptyableLoadingState { + var isLoading: Bool { + switch self { + case .loading, .idle: + true + default: + false + } + } + + var isResult: Bool { + switch self { + case .result: + true + default: + false + } + } + + var isEmpty: Bool { + switch self { + case .empty: + true + default: + false + } + } + + var hasContent: Bool { + switch self { + case .result: + true + default: + false + } + } + + var successResult: Result? { + switch self { + case let .result(result): + result + default: + nil + } + } + + var failureError: Error? { + switch self { + case let .error(error): + error + default: + nil + } + } +} + +extension EmptyableLoadingState: Equatable where Result: Equatable { + public static func == (lhs: EmptyableLoadingState, rhs: EmptyableLoadingState) -> Bool { + switch (lhs, rhs) { + case (.idle, .idle): + true + case (.loading, .loading): + true + case (.empty, .empty): + true + case let (.result(lhsResult), .result(rhsResult)): + lhsResult == rhsResult + case let (.error(lhsError), .error(rhsError)): + lhsError.localizedDescription == rhsError.localizedDescription + default: + false + } + } +} diff --git a/Sources/OversizeCore/Extensions/Result/LoadingState.swift b/Sources/OversizeCore/Extensions/Result/LoadingState.swift new file mode 100644 index 0000000..23e62d5 --- /dev/null +++ b/Sources/OversizeCore/Extensions/Result/LoadingState.swift @@ -0,0 +1,88 @@ +// +// Copyright © 2025 Alexander Romanov +// LoadingState.swift, created on 11.07.2025 +// + +import Foundation + +public enum LoadingState: Sendable { + case idle + case loading + case result(Result) + case error(Error) +} + +public extension LoadingState { + var isLoading: Bool { + switch self { + case .loading, .idle: + true + default: + false + } + } + + var isResult: Bool { + switch self { + case .result: + true + default: + false + } + } + + @available(*, deprecated, message: "Use result instead", renamed: "result") + var successResult: Result? { + switch self { + case let .result(result): + result + default: + nil + } + } + + var result: Result? { + switch self { + case let .result(result): + result + default: + nil + } + } + + @available(*, deprecated, message: "Use error instead", renamed: "error") + var failureError: Error? { + switch self { + case let .error(error): + error + default: + nil + } + } + + var error: Error? { + switch self { + case let .error(error): + error + default: + nil + } + } +} + +extension LoadingState: Equatable where Result: Equatable { + public static func == (lhs: LoadingState, rhs: LoadingState) -> Bool { + switch (lhs, rhs) { + case (.idle, .idle): + true + case (.loading, .loading): + true + case let (.result(lhsResult), .result(rhsResult)): + lhsResult == rhsResult + case let (.error(lhsError), .error(rhsError)): + lhsError.localizedDescription == rhsError.localizedDescription + default: + false + } + } +} diff --git a/Sources/OversizeCore/Extensions/Result/ResultExtension.swift b/Sources/OversizeCore/Extensions/Result/ResultExtension.swift new file mode 100644 index 0000000..1c83700 --- /dev/null +++ b/Sources/OversizeCore/Extensions/Result/ResultExtension.swift @@ -0,0 +1,35 @@ +// +// Copyright © 2024 Alexander Romanov +// File.swift, created on 27.11.2024 +// + +import Foundation + +public extension Result { + var failureError: Failure? { + switch self { + case let .failure(error): error + case .success: nil + } + } + + var successResult: Success? { + switch self { + case .failure: nil + case let .success(value): value + } + } +} + +public extension Result { + var isFailure: Bool { + !isSuccess + } + + var isSuccess: Bool { + switch self { + case .failure: false + case .success: true + } + } +} diff --git a/Sources/OversizeCore/Extensions/Result/SearchableLoadingState.swift b/Sources/OversizeCore/Extensions/Result/SearchableLoadingState.swift new file mode 100644 index 0000000..55553c4 --- /dev/null +++ b/Sources/OversizeCore/Extensions/Result/SearchableLoadingState.swift @@ -0,0 +1,131 @@ +// +// Copyright © 2025 Alexander Romanov +// SearchableLoadingState.swift, created on 02.08.2025 +// + +import Foundation + +public enum SearchableLoadingState: Sendable { + case idle + case loading + case result(Result) + case search(query: String) + case searchResult(query: String, result: Result) + case searchEmpty(query: String) + case empty + case error(Error) +} + +public extension SearchableLoadingState { + var isLoading: Bool { + switch self { + case .loading, .search, .idle: + true + default: + false + } + } + + var isSearching: Bool { + switch self { + case .search: + true + default: + false + } + } + + var isResult: Bool { + switch self { + case .result: + true + default: + false + } + } + + var isSearchResult: Bool { + switch self { + case .searchResult: + true + default: + false + } + } + + var isSearchEmpty: Bool { + switch self { + case .searchEmpty: + true + default: + false + } + } + + var hasContent: Bool { + switch self { + case .result, .searchResult: + true + default: + false + } + } + + var successResult: Result? { + switch self { + case let .result(result): + result + case let .searchResult(_, result): + result + default: + nil + } + } + + var searchQuery: String? { + switch self { + case let .search(query): + query + case let .searchResult(query, _): + query + case let .searchEmpty(query): + query + default: + nil + } + } + + var failureError: Error? { + switch self { + case let .error(error): + error + default: + nil + } + } +} + +extension SearchableLoadingState: Equatable where Result: Equatable { + public static func == (lhs: SearchableLoadingState, rhs: SearchableLoadingState) -> Bool { + switch (lhs, rhs) { + case (.idle, .idle): + true + case (.loading, .loading): + true + case let (.result(lhsResult), .result(rhsResult)): + lhsResult == rhsResult + case let (.search(lhsQuery), .search(rhsQuery)): + lhsQuery == rhsQuery + case let (.searchResult(lhsQuery, lhsResult), .searchResult(rhsQuery, rhsResult)): + lhsQuery == rhsQuery && lhsResult == rhsResult + case let (.searchEmpty(lhsQuery), .searchEmpty(rhsQuery)): + lhsQuery == rhsQuery + case (.empty, .empty): + true + case let (.error(lhsError), .error(rhsError)): + lhsError.localizedDescription == rhsError.localizedDescription + default: + false + } + } +} diff --git a/Sources/OversizeCore/Extensions/Swift/Array+Extension.swift b/Sources/OversizeCore/Extensions/Swift/Array+Extension.swift index 9a24ab0..2f30ebb 100644 --- a/Sources/OversizeCore/Extensions/Swift/Array+Extension.swift +++ b/Sources/OversizeCore/Extensions/Swift/Array+Extension.swift @@ -10,12 +10,12 @@ import Foundation public extension Sequence where Iterator.Element == Int { /// Converts a sequence of integers to an array of strings. - /// + /// /// This computed property transforms each integer in the sequence to its /// string representation, useful for UI display or data serialization. - /// + /// /// - Returns: An array of strings representing the integer values - /// + /// /// Example: /// ```swift /// let numbers = [1, 2, 3, 4, 5] @@ -34,12 +34,12 @@ public extension Sequence where Iterator.Element == Int { public extension Sequence where Iterator.Element == CGFloat { /// Calculates the sum of all CGFloat values in the sequence. - /// + /// /// This computed property adds all values in the sequence and returns /// the total sum, useful for calculating totals in UI layout or graphics operations. - /// + /// /// - Returns: The sum of all CGFloat values in the sequence - /// + /// /// Example: /// ```swift /// let values: [CGFloat] = [10.5, 20.0, 15.75] @@ -54,12 +54,12 @@ public extension Sequence where Iterator.Element == CGFloat { public extension Sequence where Iterator.Element == Int { /// Calculates the sum of all integer values in the sequence. - /// + /// /// This computed property adds all values in the sequence and returns /// the total sum, useful for calculating counts or totals. - /// + /// /// - Returns: The sum of all integer values in the sequence - /// + /// /// Example: /// ```swift /// let numbers = [1, 2, 3, 4, 5] @@ -74,12 +74,12 @@ public extension Sequence where Iterator.Element == Int { public extension Sequence where Iterator.Element == Double { /// Calculates the sum of all double values in the sequence. - /// + /// /// This computed property adds all values in the sequence and returns /// the total sum, useful for precise numerical calculations. - /// + /// /// - Returns: The sum of all double values in the sequence - /// + /// /// Example: /// ```swift /// let values = [1.5, 2.7, 3.8] @@ -94,12 +94,12 @@ public extension Sequence where Iterator.Element == Double { public extension Sequence where Iterator.Element == Float { /// Calculates the sum of all float values in the sequence. - /// + /// /// This computed property adds all values in the sequence and returns /// the total sum, useful for floating-point calculations. - /// + /// /// - Returns: The sum of all float values in the sequence - /// + /// /// Example: /// ```swift /// let values: [Float] = [1.5, 2.7, 3.8] @@ -116,13 +116,13 @@ public extension Sequence where Iterator.Element == Float { public extension Sequence where Iterator.Element == CGFloat { /// Performs sequential subtraction starting from a given value. - /// + /// /// This function takes a starting value and subtracts each element in the /// sequence from it sequentially, useful for cumulative calculations. - /// + /// /// - Parameter start: The initial value to subtract from /// - Returns: The result after subtracting all values in the sequence - /// + /// /// Example: /// ```swift /// let values: [CGFloat] = [1.0, 2.0, 3.0] @@ -137,13 +137,13 @@ public extension Sequence where Iterator.Element == CGFloat { public extension Sequence where Iterator.Element == Int { /// Performs sequential subtraction starting from a given value. - /// + /// /// This function takes a starting value and subtracts each element in the /// sequence from it sequentially, useful for cumulative calculations. - /// + /// /// - Parameter start: The initial value to subtract from /// - Returns: The result after subtracting all values in the sequence - /// + /// /// Example: /// ```swift /// let numbers = [1, 2, 3] @@ -158,13 +158,13 @@ public extension Sequence where Iterator.Element == Int { public extension Sequence where Iterator.Element == Double { /// Performs sequential subtraction starting from a given value. - /// + /// /// This function takes a starting value and subtracts each element in the /// sequence from it sequentially, useful for precise numerical calculations. - /// + /// /// - Parameter start: The initial value to subtract from /// - Returns: The result after subtracting all values in the sequence - /// + /// /// Example: /// ```swift /// let values = [1.5, 2.5, 3.0] @@ -179,13 +179,13 @@ public extension Sequence where Iterator.Element == Double { public extension Sequence where Iterator.Element == Float { /// Performs sequential subtraction starting from a given value. - /// + /// /// This function takes a starting value and subtracts each element in the /// sequence from it sequentially, useful for floating-point calculations. - /// + /// /// - Parameter start: The initial value to subtract from /// - Returns: The result after subtracting all values in the sequence - /// + /// /// Example: /// ```swift /// let values: [Float] = [1.5, 2.5, 3.0] @@ -202,21 +202,21 @@ public extension Sequence where Iterator.Element == Float { public extension Array where Element: Equatable { /// Moves an element from one position to another within the array. - /// + /// /// This mutating function repositions an element within the array by removing /// it from its current position and inserting it at the new position. - /// + /// /// - Parameters: /// - fromPosition: The current index of the element to move /// - toPosition: The target index where the element should be moved - /// + /// /// Example: /// ```swift /// var items = ["A", "B", "C", "D"] /// items.move(fromPosition: 0, toPosition: 2) /// // items is now ["B", "C", "A", "D"] /// ``` - /// + /// /// - Warning: Ensure both indices are valid to avoid runtime errors. mutating func move(fromPosition: Int, toPosition: Int) { let object = self[fromPosition] @@ -225,13 +225,13 @@ public extension Array where Element: Equatable { } /// Removes the first occurrence of the specified element from the array. - /// + /// /// This mutating function searches for the first element equal to the provided /// object and removes it from the array if found. - /// + /// /// - Parameter object: The element to remove from the array /// - Returns: The index where the element was found and removed, or `nil` if not found - /// + /// /// Example: /// ```swift /// var items = ["A", "B", "C", "B"] @@ -247,15 +247,15 @@ public extension Array where Element: Equatable { } /// Replaces an existing element or inserts it at a specified position. - /// + /// /// This mutating function either replaces an existing element (if found) or /// inserts it at the specified index. If no index is provided and the element /// doesn't exist, it's appended to the end. - /// + /// /// - Parameters: /// - object: The element to replace or insert /// - index: The index to insert at if the element is not found (optional) - /// + /// /// Example: /// ```swift /// var items = ["A", "B", "C"] @@ -273,12 +273,12 @@ public extension Array where Element: Equatable { } /// Returns a new array with duplicate elements removed. - /// + /// /// This function creates a new array containing only unique elements, /// preserving the order of first occurrence for each element. - /// + /// /// - Returns: A new array with duplicates removed - /// + /// /// Example: /// ```swift /// let items = ["A", "B", "A", "C", "B"] @@ -299,12 +299,12 @@ public extension Array where Element: Equatable { extension Array: @retroactive RawRepresentable where Element: Codable { /// Creates an array from a JSON string representation. - /// + /// /// This initializer attempts to decode a JSON string into an array of /// codable elements, useful for data persistence and serialization. - /// + /// /// - Parameter rawValue: JSON string representation of the array - /// + /// /// Example: /// ```swift /// let jsonString = "[\"A\", \"B\", \"C\"]" @@ -320,12 +320,12 @@ extension Array: @retroactive RawRepresentable where Element: Codable { } /// Returns the JSON string representation of the array. - /// + /// /// This computed property encodes the array to a JSON string, useful for /// data storage and transmission. - /// + /// /// - Returns: JSON string representation, or "[]" if encoding fails - /// + /// /// Example: /// ```swift /// let array = ["A", "B", "C"] @@ -345,13 +345,13 @@ extension Array: @retroactive RawRepresentable where Element: Codable { public extension Array { /// Safely accesses an element at the specified index. - /// + /// /// This function provides bounds-safe access to array elements, returning /// the element if the index is valid or nil if it's out of bounds. - /// + /// /// - Parameter index: The index of the element to access /// - Returns: The element at the index if valid, `nil` otherwise - /// + /// /// Example: /// ```swift /// let items = ["A", "B", "C"] @@ -371,15 +371,15 @@ public extension Array { public extension BidirectionalCollection where Iterator.Element: Equatable { /// Returns the element that comes after the specified item in the collection. - /// + /// /// This function finds the given item in the collection and returns the next element. /// Optionally supports looping back to the first element after the last one. - /// + /// /// - Parameters: /// - item: The item to find in the collection /// - loop: Whether to loop back to the first element after the last (default: false) /// - Returns: The next element after the specified item, or `nil` if not found or at end - /// + /// /// Example: /// ```swift /// let items = ["A", "B", "C", "D"] @@ -402,15 +402,15 @@ public extension BidirectionalCollection where Iterator.Element: Equatable { } /// Returns the element that comes before the specified item in the collection. - /// + /// /// This function finds the given item in the collection and returns the previous element. /// Optionally supports looping back to the last element before the first one. - /// + /// /// - Parameters: /// - item: The item to find in the collection /// - loop: Whether to loop back to the last element before the first (default: false) /// - Returns: The previous element before the specified item, or `nil` if not found or at start - /// + /// /// Example: /// ```swift /// let items = ["A", "B", "C", "D"] @@ -437,18 +437,18 @@ public extension BidirectionalCollection where Iterator.Element: Equatable { public extension Array { /// Returns a new array with non-nil elements from an array of optionals. - /// + /// /// This function provides a convenient way to compact an array of optional values, /// removing all nil values and unwrapping the remaining elements. - /// + /// /// - Returns: Array of unwrapped non-nil elements - /// + /// /// Example: /// ```swift /// let optionals: [String?] = ["A", nil, "B", nil, "C"] /// let compacted: [String] = optionals.compacted() // ["A", "B", "C"] /// ``` - /// + /// /// - Note: This is a generic constraint that only applies when Element is Optional. func compacted() -> [T] where Element == T? { compactMap { $0 } diff --git a/Sources/OversizeCore/Extensions/Swift/Bool+Extension.swift b/Sources/OversizeCore/Extensions/Swift/Bool+Extension.swift index 9dcb3c3..39b9113 100644 --- a/Sources/OversizeCore/Extensions/Swift/Bool+Extension.swift +++ b/Sources/OversizeCore/Extensions/Swift/Bool+Extension.swift @@ -9,13 +9,13 @@ import Foundation public extension Bool { /// Indicates whether the app is running on iOS 16 or later. - /// + /// /// This static computed property provides a convenient way to check iOS version /// availability for features that require iOS 16 or later. Returns true if the /// current iOS version is 16.0 or higher. - /// + /// /// - Returns: `true` if running on iOS 16+, `false` otherwise - /// + /// /// Example: /// ```swift /// if Bool.iOS16 { @@ -35,13 +35,13 @@ public extension Bool { } /// Indicates whether the app is running on iOS 17 or later. - /// + /// /// This static computed property provides a convenient way to check iOS version /// availability for features that require iOS 17 or later. Returns true if the /// current iOS version is 17.0 or higher. - /// + /// /// - Returns: `true` if running on iOS 17+, `false` otherwise - /// + /// /// Example: /// ```swift /// if Bool.iOS17 { @@ -58,13 +58,13 @@ public extension Bool { } /// Indicates whether the app is running on iOS 18 or later. - /// + /// /// This static computed property provides a convenient way to check iOS version /// availability for features that require iOS 18 or later. Returns true if the /// current iOS version is 18.0 or higher. - /// + /// /// - Returns: `true` if running on iOS 18+, `false` otherwise - /// + /// /// Example: /// ```swift /// if Bool.iOS18 { @@ -81,13 +81,13 @@ public extension Bool { } /// Indicates whether the app is running on iOS 26 or later. - /// + /// /// This static computed property provides a convenient way to check iOS version /// availability for features that require iOS 26 or later. Returns true if the /// current iOS version is 26.0 or higher. - /// + /// /// - Returns: `true` if running on iOS 26+, `false` otherwise - /// + /// /// Example: /// ```swift /// if Bool.iOS26 { @@ -95,7 +95,7 @@ public extension Bool { /// enableFutureFeatures() /// } /// ``` - /// + /// /// - Note: This is a forward-looking availability check for future iOS versions. static var iOS26: Bool { if #available(iOS 26, *) { diff --git a/Sources/OversizeCore/Extensions/Swift/Date+Extension.swift b/Sources/OversizeCore/Extensions/Swift/Date+Extension.swift index 6b222cf..dc091a8 100644 --- a/Sources/OversizeCore/Extensions/Swift/Date+Extension.swift +++ b/Sources/OversizeCore/Extensions/Swift/Date+Extension.swift @@ -7,79 +7,93 @@ import Foundation public extension Date { func startOfMonth() -> Date { - Calendar.current.date(from: Calendar.current.dateComponents([.year, .month], from: Calendar.current.startOfDay(for: self)))! + Calendar.current.date(from: Calendar.current.dateComponents([.year, .month], from: Calendar.current.startOfDay(for: self))) ?? self } func endOfMonth() -> Date { - Calendar.current.date(byAdding: DateComponents(month: 1, day: -1), to: startOfMonth())! + Calendar.current.date(byAdding: DateComponents(month: 1, day: -1), to: startOfMonth()) ?? self } } public extension Date { - static var yesterday: Date { Date().dayBefore } - static var tomorrow: Date { Date().dayAfter } + static var yesterday: Date { + Date().dayBefore + } + + static var tomorrow: Date { + Date().dayAfter + } + var dayBefore: Date { - Calendar.current.date(byAdding: .day, value: -1, to: noon)! + Calendar.current.date(byAdding: .day, value: -1, to: noon) ?? self } var dayAfter: Date { - Calendar.current.date(byAdding: .day, value: 1, to: noon)! + Calendar.current.date(byAdding: .day, value: 1, to: noon) ?? self } var weekAfter: Date { - Calendar.current.date(byAdding: .day, value: 7, to: noon)! + Calendar.current.date(byAdding: .weekOfYear, value: 1, to: noon) ?? self } var weekBefore: Date { - Calendar.current.date(byAdding: .day, value: -7, to: noon)! + Calendar.current.date(byAdding: .weekOfYear, value: -1, to: noon) ?? self } var hour: Date { - Calendar.current.date(bySettingHour: 1, minute: 0, second: 0, of: self)! + Calendar.current.date(byAdding: .hour, value: 1, to: self) ?? self } var year: Date { - Calendar.current.date(byAdding: .year, value: 1, to: self)! + Calendar.current.date(byAdding: .year, value: 1, to: self) ?? self } var yearBefore: Date { - Calendar.current.date(byAdding: .year, value: -1, to: self)! + Calendar.current.date(byAdding: .year, value: -1, to: self) ?? self } var month: Date { - Calendar.current.date(byAdding: .month, value: 1, to: self)! + Calendar.current.date(byAdding: .month, value: 1, to: self) ?? self } var minute: Date { - Calendar.current.date(byAdding: .minute, value: 1, to: self)! + Calendar.current.date(byAdding: .minute, value: 1, to: self) ?? self } var minuteBefore: Date { - Calendar.current.date(byAdding: .minute, value: -1, to: self)! + Calendar.current.date(byAdding: .minute, value: -1, to: self) ?? self } var halfHour: Date { - Calendar.current.date(byAdding: .minute, value: 30, to: self)! + Calendar.current.date(byAdding: .minute, value: 30, to: self) ?? self } var monthBefore: Date { - Calendar.current.date(byAdding: .day, value: -30, to: self)! + Calendar.current.date(byAdding: .month, value: -1, to: self) ?? self } var halfYearBefore: Date { - Calendar.current.date(byAdding: .month, value: -6, to: self)! + Calendar.current.date(byAdding: .month, value: -6, to: self) ?? self } var quarterBefore: Date { - Calendar.current.date(byAdding: .month, value: -3, to: self)! + Calendar.current.date(byAdding: .month, value: -3, to: self) ?? self } var noon: Date { - Calendar.current.date(bySettingHour: 12, minute: 0, second: 0, of: self)! + Calendar.current.date(bySettingHour: 12, minute: 0, second: 0, of: self) ?? self } var midnight: Date { - Calendar.current.date(bySettingHour: 0, minute: 0, second: 0, of: self)! + Calendar.current.date(bySettingHour: 0, minute: 0, second: 0, of: self) ?? self + } + + var endOfDay: Date { + Calendar.current.date(bySettingHour: 23, minute: 59, second: 59, of: self) ?? self + } + + var startOfDay: Date { + Calendar.current.startOfDay(for: self) } var monthNumber: Int { @@ -152,8 +166,7 @@ public extension Date { func component(_ type: Calendar.Component) -> Int { let calendar = Calendar.current - let t = calendar.component(type, from: self) - return t + return calendar.component(type, from: self) } } diff --git a/Sources/OversizeCore/Extensions/Swift/Number+Extension.swift b/Sources/OversizeCore/Extensions/Swift/Number+Extension.swift index 348c980..0d5f45a 100644 --- a/Sources/OversizeCore/Extensions/Swift/Number+Extension.swift +++ b/Sources/OversizeCore/Extensions/Swift/Number+Extension.swift @@ -10,12 +10,12 @@ import SwiftUI public extension Int { /// Returns the string representation of the integer. - /// + /// /// This computed property provides a convenient way to convert an integer /// to its string representation. - /// + /// /// - Returns: String representation of the integer - /// + /// /// Example: /// ```swift /// let number = 42 @@ -26,24 +26,24 @@ public extension Int { } /// Returns the appropriate word form for the given integer based on Slavic language rules. - /// + /// /// This function implements Slavic language pluralization rules (primarily Russian) /// to select the correct word form based on the numeric value. It's commonly used /// for displaying counts with proper grammatical forms. - /// + /// /// - Parameter titles: Array of word forms in order [singular, few, many] /// - Returns: The appropriate word form for the number - /// + /// /// Example: /// ```swift /// let count = 5 /// let forms = ["item", "items", "items"] // [singular, few, many] /// let word = count.wordFormat(titles: forms) // "items" /// ``` - /// + /// /// - Note: This follows Slavic pluralization rules where: /// - Numbers ending in 1 (except 11) use singular form (index 0) - /// - Numbers ending in 2-4 (except 12-14) use few form (index 1) + /// - Numbers ending in 2-4 (except 12-14) use few form (index 1) /// - All other numbers use many form (index 2) func wordFormat(titles: [String]) -> String { let cases = [2, 0, 1, 1, 1, 2] @@ -60,15 +60,15 @@ public extension Int { } /// Returns a formatted string with the number and appropriate word form. - /// + /// /// This function combines the integer with its appropriate word form using /// a custom format string. - /// + /// /// - Parameters: /// - formats: Array of word forms for pluralization /// - format: Format string template with placeholders for number and word /// - Returns: Formatted string with number and word - /// + /// /// Example: /// ```swift /// let count = 3 @@ -82,13 +82,13 @@ public extension Int { } /// Returns a formatted string using the word form as the format template. - /// + /// /// This function uses the selected word form itself as the format string, /// allowing for more complex formatting within the word forms. - /// + /// /// - Parameter formats: Array of format strings for pluralization /// - Returns: Formatted string using the selected format - /// + /// /// Example: /// ```swift /// let count = 1 @@ -106,12 +106,12 @@ public extension Int { public extension Double { /// Returns the string representation of the double. - /// + /// /// This computed property provides a convenient way to convert a double /// to its string representation using the default formatting. - /// + /// /// - Returns: String representation of the double - /// + /// /// Example: /// ```swift /// let number = 42.5 @@ -124,12 +124,12 @@ public extension Double { public extension Double { /// Returns the string representation of the double without decimal places. - /// + /// /// This computed property formats the double as a whole number string, /// rounding to the nearest integer and removing the decimal point. - /// + /// /// - Returns: String representation without decimal places - /// + /// /// Example: /// ```swift /// let number = 42.7 @@ -142,12 +142,12 @@ public extension Double { public extension Double { /// Returns the string representation of the double with one decimal place. - /// + /// /// This computed property formats the double to display exactly one decimal place, /// useful for consistent numeric display in user interfaces. - /// + /// /// - Returns: String representation with one decimal place - /// + /// /// Example: /// ```swift /// let number = 42.567 @@ -162,12 +162,12 @@ public extension Double { public extension Float { /// Returns the string representation of the float. - /// + /// /// This computed property provides a convenient way to convert a float /// to its string representation using the default formatting. - /// + /// /// - Returns: String representation of the float - /// + /// /// Example: /// ```swift /// let number: Float = 42.5 @@ -180,12 +180,12 @@ public extension Float { public extension Float { /// Returns the string representation of the float without decimal places. - /// + /// /// This computed property formats the float as a whole number string, /// rounding to the nearest integer and removing the decimal point. - /// + /// /// - Returns: String representation without decimal places - /// + /// /// Example: /// ```swift /// let number: Float = 42.7 @@ -200,13 +200,13 @@ public extension Float { public extension CGFloat { /// Returns the string representation of the CGFloat without decimal places. - /// + /// /// This computed property formats the CGFloat as a whole number string, /// rounding to the nearest integer and removing the decimal point. /// Commonly used for UI measurements and layout calculations. - /// + /// /// - Returns: String representation without decimal places - /// + /// /// Example: /// ```swift /// let width: CGFloat = 120.8 @@ -217,24 +217,24 @@ public extension CGFloat { } /// Returns a temperature string representation with proper formatting. - /// + /// /// This computed property formats the CGFloat as a temperature value with /// special handling for zero and negative values. It uses the proper minus /// sign (−) for negative temperatures and removes the minus sign from zero. - /// + /// /// - Returns: Formatted temperature string with degree symbol - /// + /// /// Example: /// ```swift /// let temp1: CGFloat = 23.7 /// let temp2: CGFloat = -5.2 /// let temp3: CGFloat = -0.0 - /// + /// /// print(temp1.toStringTemperature) // "24°" /// print(temp2.toStringTemperature) // "−5°" /// print(temp3.toStringTemperature) // "0°" /// ``` - /// + /// /// - Note: Uses the Unicode minus sign (−) for negative values instead of hyphen (-) var toStringTemperature: String { let conv = rounded() @@ -253,13 +253,13 @@ public extension CGFloat { public extension Decimal { /// Returns the string representation of the decimal value. - /// + /// /// This computed property converts the Decimal to a string using /// NSDecimalNumber for precise decimal representation without /// floating-point precision issues. - /// + /// /// - Returns: String representation of the decimal value - /// + /// /// Example: /// ```swift /// let decimal = Decimal(string: "123.456")! diff --git a/Sources/OversizeCore/Extensions/Swift/Optional+Extension.swift b/Sources/OversizeCore/Extensions/Swift/Optional+Extension.swift index 0ec9332..3938322 100644 --- a/Sources/OversizeCore/Extensions/Swift/Optional+Extension.swift +++ b/Sources/OversizeCore/Extensions/Swift/Optional+Extension.swift @@ -9,18 +9,18 @@ import Foundation public extension String? { /// Returns the string value or an empty string if nil. - /// + /// /// This computed property provides a safe way to unwrap optional strings, /// returning an empty string instead of nil. Useful for UI display where /// you want to show something rather than nothing. - /// + /// /// - Returns: The unwrapped string value, or "" if the optional is nil - /// + /// /// Example: /// ```swift /// let optionalName: String? = nil /// let displayName = optionalName.valueOrEmpty // "" - /// + /// /// let actualName: String? = "John" /// let displayName2 = actualName.valueOrEmpty // "John" /// ``` @@ -36,18 +36,18 @@ public extension String? { public extension Date? { /// Returns the date value or the current date if nil. - /// + /// /// This computed property provides a safe way to unwrap optional dates, /// returning the current date instead of nil. Useful when you need a /// valid date for operations or display. - /// + /// /// - Returns: The unwrapped date value, or current Date() if the optional is nil - /// + /// /// Example: /// ```swift /// let optionalDate: Date? = nil /// let safeDate = optionalDate.valueOrEmpty // Current date - /// + /// /// let actualDate: Date? = Date(timeIntervalSince1970: 1000000) /// let safeDate2 = actualDate.valueOrEmpty // The actual date /// ``` @@ -63,18 +63,18 @@ public extension Date? { public extension Float? { /// Returns the float value or zero if nil. - /// + /// /// This computed property provides a safe way to unwrap optional floats, /// returning zero instead of nil. Useful for calculations where you /// want to treat nil as a zero value. - /// + /// /// - Returns: The unwrapped float value, or 0.0 if the optional is nil - /// + /// /// Example: /// ```swift /// let optionalPrice: Float? = nil /// let price = optionalPrice.valueOrEmpty // 0.0 - /// + /// /// let actualPrice: Float? = 19.99 /// let price2 = actualPrice.valueOrEmpty // 19.99 /// ``` @@ -90,18 +90,18 @@ public extension Float? { public extension Double? { /// Returns the double value or zero if nil. - /// + /// /// This computed property provides a safe way to unwrap optional doubles, /// returning zero instead of nil. Useful for mathematical calculations /// where you want to treat nil as a zero value. - /// + /// /// - Returns: The unwrapped double value, or 0.0 if the optional is nil - /// + /// /// Example: /// ```swift /// let optionalScore: Double? = nil /// let score = optionalScore.valueOrEmpty // 0.0 - /// + /// /// let actualScore: Double? = 95.5 /// let score2 = actualScore.valueOrEmpty // 95.5 /// ``` @@ -117,18 +117,18 @@ public extension Double? { public extension Bool? { /// Returns the boolean value or false if nil. - /// + /// /// This computed property provides a safe way to unwrap optional booleans, /// returning false instead of nil. Useful for conditional logic where /// you want to treat nil as false (conservative/safe default). - /// + /// /// - Returns: The unwrapped boolean value, or false if the optional is nil - /// + /// /// Example: /// ```swift /// let optionalEnabled: Bool? = nil /// let isEnabled = optionalEnabled.valueOrFalse // false - /// + /// /// let actualEnabled: Bool? = true /// let isEnabled2 = actualEnabled.valueOrFalse // true /// ``` @@ -140,18 +140,18 @@ public extension Bool? { } /// Returns the boolean value or true if nil. - /// + /// /// This computed property provides a safe way to unwrap optional booleans, /// returning true instead of nil. Useful for conditional logic where /// you want to treat nil as true (permissive/optimistic default). - /// + /// /// - Returns: The unwrapped boolean value, or true if the optional is nil - /// + /// /// Example: /// ```swift /// let optionalAllowed: Bool? = nil /// let isAllowed = optionalAllowed.valueOrTrue // true - /// + /// /// let actualAllowed: Bool? = false /// let isAllowed2 = actualAllowed.valueOrTrue // false /// ``` diff --git a/Sources/OversizeCore/Extensions/Swift/String+Extension.swift b/Sources/OversizeCore/Extensions/Swift/String+Extension.swift index 0079416..8dbf514 100644 --- a/Sources/OversizeCore/Extensions/Swift/String+Extension.swift +++ b/Sources/OversizeCore/Extensions/Swift/String+Extension.swift @@ -12,12 +12,12 @@ import SwiftUI public extension String { /// Returns a new string with leading and trailing whitespace and newlines removed. - /// + /// /// This computed property trims whitespace and newline characters from both ends /// of the string, useful for cleaning user input or processing text data. - /// + /// /// - Returns: A new string with whitespace and newlines trimmed - /// + /// /// Example: /// ```swift /// let text = " Hello World \n" @@ -29,13 +29,13 @@ public extension String { } /// Validates if the string is a properly formatted email address. - /// + /// /// This computed property uses regular expression validation to check if the string /// matches a valid email format. It checks for the standard email pattern including /// local part, @ symbol, domain, and top-level domain. - /// + /// /// - Returns: `true` if the string is a valid email format, `false` otherwise - /// + /// /// Example: /// ```swift /// let email1 = "user@example.com" @@ -50,12 +50,12 @@ public extension String { } /// Validates if the string is a properly formatted URL. - /// + /// /// This computed property uses regular expression validation to check if the string /// matches a valid HTTP or HTTPS URL format, including domain, port, path, and query parameters. - /// + /// /// - Returns: `true` if the string is a valid URL format, `false` otherwise - /// + /// /// Example: /// ```swift /// let url1 = "https://www.example.com/path?param=value" @@ -70,12 +70,12 @@ public extension String { } /// Validates if the string contains only alphabetic characters and underscores. - /// + /// /// This computed property checks if the string consists only of letters (A-Z, a-z) /// and underscore characters, useful for validating identifiers or names. - /// + /// /// - Returns: `true` if the string contains only alphabetic characters and underscores, `false` otherwise - /// + /// /// Example: /// ```swift /// let valid = "Hello_World" @@ -90,18 +90,18 @@ public extension String { } /// Returns a URL-encoded version of the string. - /// + /// /// This computed property applies percent encoding to the string, making it safe /// for use in URLs by encoding special characters. - /// + /// /// - Returns: A URL-encoded string - /// + /// /// Example: /// ```swift /// let text = "Hello World!" /// let encoded = text.urlEncode // "Hello%20World%21" /// ``` - /// + /// /// - Warning: This method force-unwraps the result. Ensure the string is valid for encoding. var urlEncode: String { let originalString = self @@ -110,17 +110,17 @@ public extension String { } /// Converts the string to a URL object. - /// + /// /// This computed property attempts to create a URL from the string, with special /// handling for URLs that start with "//" (protocol-relative URLs). - /// + /// /// - Returns: A URL object if the string is a valid URL, `nil` otherwise - /// + /// /// Example: /// ```swift /// let urlString = "https://example.com" /// let url = urlString.url // URL(string: "https://example.com") - /// + /// /// let protocolRelative = "//example.com" /// let url2 = protocolRelative.url // URL(string: "http://example.com") /// ``` @@ -138,13 +138,13 @@ public extension String { } /// Converts decimal separators to match the current locale. - /// + /// /// This computed property adjusts decimal separators in the string to match /// the current locale's decimal separator (comma or period), useful for /// processing numeric strings across different locales. - /// + /// /// - Returns: A string with decimal separators adjusted for the current locale - /// + /// /// Example: /// ```swift /// // In a locale that uses comma as decimal separator @@ -166,13 +166,13 @@ public extension String { } /// Extracts only the numeric characters from the string. - /// + /// /// This computed property removes all non-numeric characters, leaving only /// decimal digits (0-9). Useful for cleaning phone numbers or extracting /// numbers from mixed content. - /// + /// /// - Returns: A string containing only numeric characters - /// + /// /// Example: /// ```swift /// let mixed = "Phone: (555) 123-4567" @@ -185,13 +185,13 @@ public extension String { } /// Extracts only the letter characters from the string. - /// + /// /// This computed property removes all non-letter characters, leaving only /// alphabetic characters (A-Z, a-z). Useful for extracting text from /// mixed alphanumeric content. - /// + /// /// - Returns: A string containing only letter characters - /// + /// /// Example: /// ```swift /// let mixed = "Hello123World456" @@ -204,12 +204,12 @@ public extension String { } /// Extracts only letters and spaces from the string. - /// + /// /// This computed property removes all characters except letters and spaces, /// useful for cleaning text input while preserving word separation. - /// + /// /// - Returns: A string containing only letters and spaces - /// + /// /// Example: /// ```swift /// let mixed = "Hello123 World456!" @@ -222,30 +222,30 @@ public extension String { } /// Converts the string to UTF-8 encoded Data. - /// + /// /// This computed property converts the string to Data using UTF-8 encoding, /// commonly needed for network operations or file I/O. - /// + /// /// - Returns: UTF-8 encoded Data representation of the string - /// + /// /// Example: /// ```swift /// let text = "Hello, World!" /// let data = text.data // Data representation /// ``` - /// + /// /// - Warning: This method force-unwraps the result. The string must be valid for UTF-8 encoding. var data: Data { self.data(using: String.Encoding.utf8) ?? Data() } /// Returns the NSRange covering the entire string. - /// + /// /// This computed property creates an NSRange that spans the entire string, /// useful for string manipulation operations that require NSRange parameters. - /// + /// /// - Returns: NSRange covering the entire string using UTF-16 encoding - /// + /// /// Example: /// ```swift /// let text = "Hello, World!" @@ -260,19 +260,19 @@ public extension String { public extension String { /// Converts the string to a Date using ISO8601 formatting. - /// + /// /// This function parses the string as an ISO8601 date format and returns /// a Date object. The format options can be customized to handle different /// ISO8601 variants. - /// + /// /// - Parameter formatOptions: ISO8601 formatting options to use for parsing /// - Returns: A Date object if parsing succeeds, `nil` otherwise - /// + /// /// Example: /// ```swift /// let dateString = "2024-01-15" /// let date = dateString.toDate() // Uses .withFullDate by default - /// + /// /// let timeString = "2024-01-15T14:30:25Z" /// let dateTime = timeString.toDate(formatOptions: [.withFullDate, .withTime]) /// ``` @@ -287,18 +287,18 @@ public extension String { public extension String { /// Converts the string to an enum case of the specified type. - /// + /// /// This generic function attempts to create an enum value from the string, /// where the string represents the raw value of the enum case. - /// + /// /// - Returns: An enum case of type T if the conversion succeeds, `nil` otherwise - /// + /// /// Example: /// ```swift /// enum Status: String { /// case active, inactive, pending /// } - /// + /// /// let statusString = "active" /// let status: Status? = statusString.enumCase() // Status.active /// ``` @@ -310,13 +310,13 @@ public extension String { } /// Finds all matches for a regular expression pattern in the string. - /// + /// /// This function searches the string for all occurrences of the specified /// regular expression pattern and returns an array of match groups for each match. - /// + /// /// - Parameter regex: The regular expression pattern to search for /// - Returns: An array of string arrays, where each inner array contains the match groups for one match - /// + /// /// Example: /// ```swift /// let text = "Contact: john@example.com or jane@test.org" @@ -347,13 +347,13 @@ public extension String { extension String { /// Returns a new string with the first letter capitalized. - /// + /// /// This function capitalizes only the first character of the string, /// leaving the rest of the string unchanged. Useful for proper name /// formatting or sentence capitalization. - /// + /// /// - Returns: A new string with the first letter capitalized - /// + /// /// Example: /// ```swift /// let text = "hello world" @@ -364,9 +364,9 @@ extension String { } /// Capitalizes the first letter of the string in place. - /// + /// /// This mutating function modifies the string by capitalizing its first character. - /// + /// /// Example: /// ```swift /// var text = "hello world" @@ -380,13 +380,13 @@ extension String { extension String { /// Returns a new string with the first letter lowercased. - /// + /// /// This function lowercases only the first character of the string, /// leaving the rest of the string unchanged. Useful for camelCase /// conversion or specific formatting requirements. - /// + /// /// - Returns: A new string with the first letter lowercased - /// + /// /// Example: /// ```swift /// let text = "Hello World" @@ -397,9 +397,9 @@ extension String { } /// Lowercases the first letter of the string in place. - /// + /// /// This mutating function modifies the string by lowercasing its first character. - /// + /// /// Example: /// ```swift /// var text = "Hello World" @@ -417,16 +417,16 @@ extension String { extension String { /// Converts HTML string to attributed string with specified font and color. - /// + /// /// This function parses HTML content and creates an NSAttributedString with /// the specified font and color applied to the entire string. Useful for /// displaying HTML content in UIKit components. - /// + /// /// - Parameters: /// - font: The UIFont to apply to the attributed string /// - color: The UIColor to apply to the attributed string /// - Returns: An NSAttributedString if HTML parsing succeeds, `nil` otherwise - /// + /// /// Example: /// ```swift /// let html = "

Hello World

" @@ -434,7 +434,7 @@ extension String { /// let color = UIColor.black /// let attributed = html.getAttributedStringFromHtml(font: font, color: color) /// ``` - /// + /// /// - Note: This function is only available on iOS, tvOS, and watchOS platforms. func getAttributedStringFromHtml(font: UIFont, color: UIColor) -> NSAttributedString? { guard let data = data(using: String.Encoding.utf16, allowLossyConversion: false) else { @@ -468,20 +468,20 @@ extension String { public extension String { /// Calculates the size required to display the string with the specified font. - /// + /// /// This function measures the bounding size needed to display the string /// using the specified font, useful for layout calculations and UI sizing. - /// + /// /// - Parameter font: The UIFont to use for measurement /// - Returns: The CGSize required to display the string - /// + /// /// Example: /// ```swift /// let text = "Hello, World!" /// let font = UIFont.systemFont(ofSize: 16) /// let size = text.size(font: font) // CGSize(width: 87.5, height: 19.0) /// ``` - /// + /// /// - Note: This function is only available on iOS, tvOS, and watchOS platforms. func size(font: UIFont) -> CGSize { let originalString = self as NSString @@ -489,20 +489,20 @@ public extension String { } /// Calculates the width required to display the string with the specified font. - /// + /// /// This function measures the width needed to display the string using the /// specified font, providing a convenient way to get just the width measurement. - /// + /// /// - Parameter font: The UIFont to use for measurement /// - Returns: The CGFloat width required to display the string - /// + /// /// Example: /// ```swift /// let text = "Hello, World!" /// let font = UIFont.systemFont(ofSize: 16) /// let width = text.width(font: font) // 87.5 /// ``` - /// + /// /// - Note: This function is only available on iOS, tvOS, and watchOS platforms. func width(font: UIFont) -> CGFloat { size(font: font).width @@ -513,21 +513,21 @@ public extension String { public extension NSAttributedString { /// Calculates the height required to display the attributed string within a given width. - /// + /// /// This function measures the height needed to display the attributed string /// when constrained to a specific width, useful for dynamic text layout /// and table view cell sizing. - /// + /// /// - Parameter width: The maximum width constraint for the text /// - Returns: The CGFloat height required to display the attributed string - /// + /// /// Example: /// ```swift /// let attributedText = NSAttributedString(string: "Long text content...") /// let maxWidth: CGFloat = 300 /// let height = attributedText.height(width: maxWidth) // Calculated height /// ``` - /// + /// /// - Note: This function is only available on iOS, tvOS, and watchOS platforms. func height(width: CGFloat) -> CGFloat { let constraintRect: CGSize = .init(width: width, height: CGFloat.greatestFiniteMagnitude) diff --git a/Sources/OversizeCore/Extensions/SwiftData/SortOrder+Extension.swift b/Sources/OversizeCore/Extensions/SwiftData/SortOrder+Extension.swift index f04bacc..17b8b95 100644 --- a/Sources/OversizeCore/Extensions/SwiftData/SortOrder+Extension.swift +++ b/Sources/OversizeCore/Extensions/SwiftData/SortOrder+Extension.swift @@ -19,5 +19,7 @@ extension SortOrder: @retroactive CaseIterable, @retroactive Identifiable { } } - public var id: String { title } + public var id: String { + title + } } diff --git a/Sources/OversizeCore/Global/Delay.swift b/Sources/OversizeCore/Global/Delay.swift index 52b28e9..9dfa367 100644 --- a/Sources/OversizeCore/Global/Delay.swift +++ b/Sources/OversizeCore/Global/Delay.swift @@ -8,21 +8,21 @@ import Foundation // MARK: - Legacy Delay Functions /// Executes a closure after a specified time interval on the main queue. -/// +/// /// This function provides a simple way to delay execution using DispatchQueue. /// The closure is executed on the main queue after the specified time interval. -/// +/// /// - Parameters: /// - time: The time interval to wait before executing the closure /// - execute: The closure to execute after the delay -/// +/// /// Example: /// ```swift /// delay(time: 2.0) { /// print("This will be printed after 2 seconds") /// } /// ``` -/// +/// /// - Note: For async/await contexts, prefer using the modern `delay(_:action:)` function. public func delay(time: TimeInterval, execute: @Sendable @escaping () -> Void) { DispatchQueue.main.asyncAfter(deadline: .now() + time, execute: execute) @@ -31,22 +31,22 @@ public func delay(time: TimeInterval, execute: @Sendable @escaping () -> Void) { // MARK: - Modern Async Delay Functions /// Suspends execution for a specified duration and then executes an async action. -/// +/// /// This function provides a modern async/await-based delay mechanism using the new /// Clock API introduced in iOS 16. The function suspends the current task for the /// specified duration before executing the provided action. -/// +/// /// - Parameters: /// - time: The duration to wait before executing the action /// - action: The async action to execute after the delay -/// +/// /// Example: /// ```swift /// await delay(.seconds(3)) { /// await updateUI() /// } /// ``` -/// +/// /// - Availability: iOS 16.0+, tvOS 16.0+, watchOS 9.0+, macOS 13.0+ @available(iOS 16.0, tvOS 16.0, watchOS 9.0, macOS 13.0, *) public func delay(_ time: ContinuousClock.Duration, action: @Sendable @escaping () async -> Void) async { @@ -59,15 +59,15 @@ public func delay(_ time: ContinuousClock.Duration, action: @Sendable @escaping } /// Suspends execution for a specified duration and then executes a MainActor action. -/// +/// /// This function is similar to `delay(_:action:)` but ensures the action is executed /// on the main actor, making it safe for UI updates. The function suspends the current /// task for the specified duration before executing the provided action on the main actor. -/// +/// /// - Parameters: /// - time: The duration to wait before executing the action /// - action: The main actor action to execute after the delay -/// +/// /// Example: /// ```swift /// await delayMain(.seconds(1)) { @@ -75,7 +75,7 @@ public func delay(_ time: ContinuousClock.Duration, action: @Sendable @escaping /// self.label.text = "Updated after delay" /// } /// ``` -/// +/// /// - Availability: iOS 16.0+, tvOS 16.0+, watchOS 9.0+, macOS 13.0+ @available(iOS 16.0, tvOS 16.0, watchOS 9.0, macOS 13.0, *) public func delayMain(_ time: ContinuousClock.Duration, action: @MainActor @Sendable @escaping () async -> Void) async { @@ -88,15 +88,15 @@ public func delayMain(_ time: ContinuousClock.Duration, action: @MainActor @Send } /// Creates a detached task that delays for a specified duration and then executes an action. -/// +/// /// This function creates a detached task that delays execution and then runs the provided /// action. Unlike the other delay functions, this one doesn't suspend the current task /// but creates a new detached task for the delay and execution. -/// +/// /// - Parameters: /// - time: The duration to wait before executing the action /// - action: The action to execute after the delay -/// +/// /// Example: /// ```swift /// delayDetached(.seconds(5)) { @@ -104,7 +104,7 @@ public func delayMain(_ time: ContinuousClock.Duration, action: @MainActor @Send /// } /// // Code continues immediately without waiting /// ``` -/// +/// /// - Availability: iOS 16.0+, tvOS 16.0+, watchOS 9.0+, macOS 13.0+ /// - Note: The action is executed in a detached task context, separate from the current task hierarchy. @available(iOS 16.0, tvOS 16.0, watchOS 9.0, macOS 13.0, *) diff --git a/Sources/OversizeCore/Global/Log.swift b/Sources/OversizeCore/Global/Log.swift index ecd13c7..a19e7e9 100644 --- a/Sources/OversizeCore/Global/Log.swift +++ b/Sources/OversizeCore/Global/Log.swift @@ -8,12 +8,12 @@ import Foundation // MARK: - Basic Logging Functions /// Logs multiple objects to the console in debug builds. -/// +/// /// This function takes multiple objects and logs them as a comma-separated string. /// Logging only occurs in debug builds (when DEBUG is defined). -/// +/// /// - Parameter objects: Multiple objects to log -/// +/// /// Example: /// ```swift /// log("User:", user.name, "Score:", score) @@ -26,17 +26,17 @@ public func log(_ objects: Any...) { } /// Logs an optional object to the console in debug builds. -/// +/// /// This function safely logs an optional value, handling nil cases gracefully. /// Logging only occurs in debug builds (when DEBUG is defined). -/// +/// /// - Parameter object: Optional object to log -/// +/// /// Example: /// ```swift /// let optionalValue: String? = "Hello" /// log(optionalValue) // Output: Hello -/// +/// /// let nilValue: String? = nil /// log(nilValue) // Output: nil /// ``` @@ -51,14 +51,14 @@ public func log(_ object: Any?) { } /// Logs a string to the console with optional terminator in debug builds. -/// +/// /// This is the base logging function that outputs text to the console. /// Logging only occurs in debug builds (when DEBUG is defined). -/// +/// /// - Parameters: /// - text: The text to log /// - terminator: Optional terminator string. If nil, uses default newline -/// +/// /// Example: /// ```swift /// log("Hello World") @@ -73,14 +73,14 @@ public func log(_ object: Any?) { // MARK: - Timestamped Logging /// Logs a string with timestamp prefix in debug builds. -/// +/// /// This function adds a timestamp prefix to the log message, useful for tracking /// when events occur during debugging. Logging only occurs in debug builds. -/// +/// /// - Parameters: /// - text: The text to log /// - terminator: Optional terminator string. If nil, uses default newline -/// +/// /// Example: /// ```swift /// logWithTime("Application started") @@ -97,14 +97,14 @@ public func logWithTime(_ text: String, terminator: String? = nil) { // MARK: - Categorized Logging Functions /// Logs a debug message with debug category prefix in debug builds. -/// +/// /// This function is used for general debugging information that helps track /// application flow and state during development. -/// +/// /// - Parameters: /// - text: The debug message to log /// - terminator: Optional terminator string. If nil, uses default newline -/// +/// /// Example: /// ```swift /// logDebug("User preferences loaded") @@ -117,14 +117,14 @@ public func logWithTime(_ text: String, terminator: String? = nil) { } /// Logs a UI-related message with UI category prefix in debug builds. -/// +/// /// This function is used for logging user interface updates, view lifecycle events, /// and other UI-related debugging information. -/// +/// /// - Parameters: /// - text: The UI message to log /// - terminator: Optional terminator string. If nil, uses default newline -/// +/// /// Example: /// ```swift /// logUI("View did appear") @@ -137,14 +137,14 @@ public func logWithTime(_ text: String, terminator: String? = nil) { } /// Logs a notice message with notice category prefix in debug builds. -/// +/// /// This function is used for important notices that require attention but /// are not errors or warnings. -/// +/// /// - Parameters: /// - text: The notice message to log /// - terminator: Optional terminator string. If nil, uses default newline -/// +/// /// Example: /// ```swift /// logNotice("Cache cleared successfully") @@ -157,14 +157,14 @@ public func logWithTime(_ text: String, terminator: String? = nil) { } /// Logs a network-related message with network category prefix in debug builds. -/// +/// /// This function is used for logging network requests, responses, and /// connectivity-related debugging information. -/// +/// /// - Parameters: /// - text: The network message to log /// - terminator: Optional terminator string. If nil, uses default newline -/// +/// /// Example: /// ```swift /// logNetwork("API request completed") @@ -177,14 +177,14 @@ public func logWithTime(_ text: String, terminator: String? = nil) { } /// Logs an informational message with info category prefix in debug builds. -/// +/// /// This function is used for general informational messages that provide /// context about application state and operations. -/// +/// /// - Parameters: /// - text: The informational message to log /// - terminator: Optional terminator string. If nil, uses default newline -/// +/// /// Example: /// ```swift /// logInfo("Database connection established") @@ -197,14 +197,14 @@ public func logWithTime(_ text: String, terminator: String? = nil) { } /// Logs a security-related message with security category prefix in debug builds. -/// +/// /// This function is used for logging security-related events, authentication /// attempts, and other security-sensitive debugging information. -/// +/// /// - Parameters: /// - text: The security message to log /// - terminator: Optional terminator string. If nil, uses default newline -/// +/// /// Example: /// ```swift /// logSecurity("User authenticated successfully") @@ -219,14 +219,14 @@ public func logWithTime(_ text: String, terminator: String? = nil) { // MARK: - Success Logging /// Logs a success message with success category prefix in debug builds. -/// +/// /// This function is used for logging successful operations and positive outcomes. /// This overload is marked as disfavored to encourage using the more specific variant. -/// +/// /// - Parameters: /// - text: The success message to log /// - terminator: Optional terminator string. If nil, uses default newline -/// +/// /// Example: /// ```swift /// logSuccess("Data saved successfully") @@ -240,14 +240,14 @@ public func logWithTime(_ text: String, terminator: String? = nil) { } /// Logs a success message with an associated object in debug builds. -/// +/// /// This function logs a success message followed by the string representation /// of the provided object, useful for debugging successful operations with data. -/// +/// /// - Parameters: /// - text: The success message to log /// - object: The object to log along with the success message -/// +/// /// Example: /// ```swift /// logSuccess("User created", object: user) @@ -264,14 +264,14 @@ public func logSuccess(_ text: String, object: Any?) { // MARK: - Warning and Error Logging /// Logs a warning message with warning category prefix in debug builds. -/// +/// /// This function is used for logging warning conditions that don't prevent /// application execution but may indicate potential issues. -/// +/// /// - Parameters: /// - text: The warning message to log /// - terminator: Optional terminator string. If nil, uses default newline -/// +/// /// Example: /// ```swift /// logWarning("Deprecated API usage detected") @@ -284,13 +284,13 @@ public func logSuccess(_ text: String, object: Any?) { } /// Logs an error message with error category prefix in debug builds. -/// +/// /// This function is used for logging error conditions and failures. -/// +/// /// - Parameters: /// - text: The error message to log /// - terminator: Optional terminator string. If nil, uses default newline -/// +/// /// Example: /// ```swift /// logError("Failed to load configuration") @@ -303,15 +303,15 @@ public func logSuccess(_ text: String, object: Any?) { } /// Logs an error message with detailed error information in debug builds. -/// +/// /// This function logs an error message along with the localized description /// and full error details for comprehensive debugging. -/// +/// /// - Parameters: /// - text: The error message to log /// - error: The Error object containing detailed error information /// - terminator: Optional terminator string. If nil, uses default newline -/// +/// /// Example: /// ```swift /// do { @@ -330,10 +330,10 @@ public func logSuccess(_ text: String, object: Any?) { } /// Logs an error message with detailed error information in debug builds. -/// +/// /// This is a convenience overload that provides a more natural parameter order /// for error logging with Error objects. -/// +/// /// - Parameters: /// - text: The error message to log /// - error: The Error object containing detailed error information @@ -343,15 +343,15 @@ public func logSuccess(_ text: String, object: Any?) { } /// Logs an error message with a string error description in debug builds. -/// +/// /// This function logs an error message along with a string-based error description /// for cases where you have error information as a string rather than an Error object. -/// +/// /// - Parameters: /// - text: The error message to log /// - error: String description of the error /// - terminator: Optional terminator string. If nil, uses default newline -/// +/// /// Example: /// ```swift /// logError("Validation failed", error: "Email address is invalid") @@ -367,13 +367,13 @@ public func logSuccess(_ text: String, object: Any?) { // MARK: - Specialized Logging /// Logs a deletion message with deleted category prefix in debug builds. -/// +/// /// This function is used for logging deletion operations and cleanup activities. -/// +/// /// - Parameters: /// - text: The deletion message to log /// - terminator: Optional terminator string. If nil, uses default newline -/// +/// /// Example: /// ```swift /// logDeleted("Temporary files cleaned up") @@ -386,14 +386,14 @@ public func logSuccess(_ text: String, object: Any?) { } /// Logs a data-related message with data category prefix in debug builds. -/// +/// /// This function is used for logging data operations, database activities, /// and data processing information. -/// +/// /// - Parameters: /// - text: The data message to log /// - terminator: Optional terminator string. If nil, uses default newline -/// +/// /// Example: /// ```swift /// logData("User preferences synchronized") @@ -406,27 +406,27 @@ public func logSuccess(_ text: String, object: Any?) { } /// Logs URL information with appropriate category prefix in debug builds. -/// +/// /// This function logs URL information, automatically detecting whether the URL /// is a file URL or web URL and using appropriate icons and formatting. -/// +/// /// - Parameters: /// - text: Optional descriptive text for the URL /// - url: The URL to log (can be nil) /// - terminator: Optional terminator string. If nil, uses default newline -/// +/// /// Example: /// ```swift /// let webURL = URL(string: "https://example.com") /// logUrl("API endpoint", url: webURL) /// // Output: 🌐 [URL] API endpoint: /// // https://example.com -/// +/// /// let fileURL = URL(fileURLWithPath: "/tmp/file.txt") /// logUrl("Temp file", url: fileURL) /// // Output: 📁 [URL] Temp file: /// // /tmp/file.txt -/// +/// /// logUrl("Invalid", url: nil) /// // Output: 🔗 [URL] Nil or not valid URL /// ``` diff --git a/Tests/OversizeCoreTests/OversizeCoreTests.swift b/Tests/OversizeCoreTests/OversizeCoreTests.swift index 7007a9e..25b320d 100644 --- a/Tests/OversizeCoreTests/OversizeCoreTests.swift +++ b/Tests/OversizeCoreTests/OversizeCoreTests.swift @@ -7,7 +7,7 @@ import XCTest final class OversizeCoreTests: XCTestCase { - func testExample() throws { + func testExample() { // This is an example of a functional test case. // Use XCTAssert and related functions to verify your tests produce the correct // results.