import Foundation import UIKit // MARK: - Download Extensions extension DownloadTask { /// Formatea el tamaño total de la descarga var formattedSize: String { let formatter = ByteCountFormatter() formatter.allowedUnits = [.useBytes, .useKB, .useMB] formatter.countStyle = .file return formatter.string(fromByteCount: Int64(imageURLs.count * 500_000)) // Estimación de 500KB por imagen } /// Retorna el tiempo estimado restante var estimatedTimeRemaining: String? { guard progress > 0 && progress < 1 else { return nil } let downloadedPages = Double(imageURLs.count) * progress let remainingPages = Double(imageURLs.count) - downloadedPages // Estimación: 2 segundos por página let estimatedSeconds = remainingPages * 2 if estimatedSeconds < 60 { return "\(Int(estimatedSeconds))s restantes" } else { let minutes = Int(estimatedSeconds / 60) return "\(min)m restantes" } } } extension DownloadManager { /// Obtiene estadísticas de descarga var downloadStats: DownloadStats { let activeCount = activeDownloads.count let completedCount = completedDownloads.count let failedCount = failedDownloads.count return DownloadStats( activeDownloads: activeCount, completedDownloads: completedCount, failedDownloads: failedCount, totalProgress: totalProgress ) } /// Verifica si hay descargas activas var hasActiveDownloads: Bool { !activeDownloads.isEmpty } /// Obtiene el número total de descargas var totalDownloads: Int { activeDownloads.count + completedDownloads.count + failedDownloads.count } } // MARK: - Download Stats Model struct DownloadStats { let activeDownloads: Int let completedDownloads: Int let failedDownloads: Int let totalProgress: Double var totalDownloads: Int { activeDownloads + completedDownloads + failedDownloads } var successRate: Double { guard totalDownloads > 0 else { return 0 } return Double(completedDownloads) / Double(totalDownloads) } } // MARK: - UIImage Extension for Compression extension UIImage { /// Comprime la imagen con una calidad específica func compressedData(quality: CGFloat = 0.8) -> Data? { return jpegData(compressionQuality: quality) } /// Redimensiona la imagen a un tamaño máximo func resized(maxWidth: CGFloat, maxHeight: CGFloat) -> UIImage? { let size = size let widthRatio = maxWidth / size.width let heightRatio = maxHeight / size.height let ratio = min(widthRatio, heightRatio) let newSize = CGSize( width: size.width * ratio, height: size.height * ratio ) UIGraphicsBeginImageContextWithOptions(newSize, false, 1.0) draw(in: CGRect(origin: .zero, size: newSize)) let resizedImage = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() return resizedImage } /// Optimiza la imagen para almacenamiento func optimizedForStorage() -> Data? { // Redimensionar si es muy grande let maxDimension: CGFloat = 2048 let resized: UIImage if size.width > maxDimension || size.height > maxDimension { resized = self.resized(maxWidth: maxDimension, maxHeight: maxDimension) ?? self } else { resized = self } // Comprimir con calidad balanceada return resized.compressedData(quality: 0.75) } } // MARK: - Notification Names extension Notification.Name { static let downloadDidStart = Notification.Name("downloadDidStart") static let downloadDidUpdate = Notification.Name("downloadDidUpdate") static let downloadDidComplete = Notification.Name("downloadDidComplete") static let downloadDidFail = Notification.Name("downloadDidFail") static let downloadDidCancel = Notification.Name("downloadDidCancel") } // MARK: - Download Progress Notification struct DownloadProgressNotification { let taskId: String let progress: Double let downloadedPages: Int let totalPages: Int } // MARK: - URLSession Extension for Download Tracking extension URLSession { /// Configura una URLSession para descargas con timeout static func downloadSession() -> URLSession { let configuration = URLSessionConfiguration.default configuration.timeoutIntervalForRequest = 30 configuration.timeoutIntervalForResource = 300 configuration.requestCachePolicy = .reloadIgnoringLocalCacheData return URLSession(configuration: configuration) } }