import Foundation /// Ejemplos de uso de APIConfig /// /// Este archivo demuestra cómo utilizar la configuración de la API /// en diferentes escenarios de la aplicación. class APIConfigExample { /// Ejemplo 1: Configurar URLSession con timeouts de APIConfig func configureURLSession() -> URLSession { let configuration = URLSessionConfiguration.default configuration.timeoutIntervalForRequest = APIConfig.defaultTimeout configuration.timeoutIntervalForResource = APIConfig.downloadTimeout return URLSession(configuration: configuration) } /// Ejemplo 2: Construir una URL completa para un endpoint func buildEndpointURL() { // Método 1: Usar la función helper let url1 = APIConfig.url(for: "manga/popular") print("URL completa: \(url1)") // Método 2: Usar urlObject para obtener un objeto URL if let url2 = APIConfig.urlObject(for: "manga/popular") { print("URL object: \(url2)") } // Método 3: Usar directamente baseURL let url3 = "\(APIConfig.basePath)/manga/popular" print("URL manual: \(url3)") } /// Ejemplo 3: Usar los endpoints predefinidos func usePredefinedEndpoints() { // Endpoint de descarga let downloadURL = APIConfig.Endpoints.download( mangaSlug: "one-piece", chapterNumber: 1089 ) print("Download endpoint: \(downloadURL)") // Endpoint de verificación let checkURL = APIConfig.Endpoints.checkDownloaded( mangaSlug: "one-piece", chapterNumber: 1089 ) print("Check endpoint: \(checkURL)") // Endpoint de imagen let imageURL = APIConfig.Endpoints.getImage( mangaSlug: "one-piece", chapterNumber: 1089, pageIndex: 0 ) print("Image endpoint: \(imageURL)") // Endpoint de health check let healthURL = APIConfig.Endpoints.health() print("Health endpoint: \(healthURL)") // Endpoint de estadísticas de almacenamiento let statsURL = APIConfig.Endpoints.storageStats() print("Storage stats endpoint: \(statsURL)") } /// Ejemplo 4: Crear una URLRequest con headers comunes func createRequest() -> URLRequest? { let endpoint = "manga/popular" guard let url = APIConfig.urlObject(for: endpoint) else { return nil } var request = URLRequest(url: url) request.timeoutInterval = APIConfig.defaultTimeout // Añadir headers comunes for (key, value) in APIConfig.commonHeaders { request.setValue(value, forHTTPHeaderField: key) } // Si se requiere autenticación // let token = "your-auth-token" // let authHeaders = APIConfig.authHeader(token: token) // for (key, value) in authHeaders { // request.setValue(value, forHTTPHeaderField: key) // } return request } /// Ejemplo 5: Validar la configuración al iniciar la app func validateConfiguration() { #if DEBUG // Imprimir configuración en debug APIConfig.printConfiguration() #endif // Validar que la configuración sea correcta guard APIConfig.isValid else { print("ERROR: Configuración de API inválida") return } print("Configuración válida: \(APIConfig.baseURL)") } /// Ejemplo 6: Hacer una request simple func makeSimpleRequest() async throws { let endpoint = "manga/popular" guard let url = APIConfig.urlObject(for: endpoint) else { print("URL inválida") return } var request = URLRequest(url: url) request.timeoutInterval = APIConfig.timeoutFor(isResourceRequest: false) for (key, value) in APIConfig.commonHeaders { request.setValue(value, forHTTPHeaderField: key) } let (data, response) = try await URLSession.shared.data(for: request) if let httpResponse = response as? HTTPURLResponse { print("Status code: \(httpResponse.statusCode)") } // Procesar data... print("Recibidos \(data.count) bytes") } /// Ejemplo 7: Usar timeouts apropiados según el tipo de request func demonstrateTimeouts() { // Request normal (usar defaultTimeout) let normalTimeout = APIConfig.timeoutFor(isResourceRequest: false) print("Normal timeout: \(normalTimeout)s") // 30.0s // Request de descarga de imagen (usar downloadTimeout) let resourceTimeout = APIConfig.timeoutFor(isResourceRequest: true) print("Resource timeout: \(resourceTimeout)s") // 300.0s } /// Ejemplo 8: Cambiar configuración según el entorno func configureForEnvironment() { #if DEBUG // En desarrollo, usar configuración local print("Modo desarrollo") // Nota: Para cambiar realmente la configuración, modificar las propiedades // estáticas en APIConfig usando compilación condicional #else // En producción, usar configuración de producción print("Modo producción") #endif } /// Ejemplo 9: Manejar errores específicos de la API func handleAPIError(errorCode: Int) { switch errorCode { case APIConfig.ErrorCodes.chapterNotFound: print("Capítulo no encontrado") case APIConfig.ErrorCodes.chapterAlreadyDownloaded: print("Capítulo ya descargado") case APIConfig.ErrorCodes.storageLimitExceeded: print("Límite de almacenamiento excedido") case APIConfig.ErrorCodes.invalidImageFormat: print("Formato de imagen inválido") case APIConfig.ErrorCodes.downloadFailed: print("Descarga fallida") default: print("Error desconocido: \(errorCode)") } } /// Ejemplo 10: Implementar retry con backoff exponencial func fetchWithRetry(endpoint: String, retryCount: Int = 0) async throws -> Data { guard let url = APIConfig.urlObject(for: endpoint) else { throw URLError(.badURL) } var request = URLRequest(url: url) request.timeoutInterval = APIConfig.defaultTimeout do { let (data, response) = try await URLSession.shared.data(for: request) if let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 { return data } else { throw URLError(.badServerResponse) } } catch { // Verificar si debemos reintentar if retryCount < APIConfig.maxRetries { // Calcular delay con backoff exponencial let delay = APIConfig.baseRetryDelay * pow(2.0, Double(retryCount)) print("Retry \(retryCount + 1) después de \(delay)s") try await Task.sleep(nanoseconds: UInt64(delay * 1_000_000_000)) return try await fetchWithRetry(endpoint: endpoint, retryCount: retryCount + 1) } else { throw error } } } } // MARK: - Usage Examples // Ejemplo de uso en una ViewModel o Service: class MangaServiceExample { func fetchPopularManga() async throws { // Usar endpoint predefinido let endpoint = "manga/popular" guard let url = APIConfig.urlObject(for: endpoint) else { return } var request = URLRequest(url: url) request.timeoutInterval = APIConfig.defaultTimeout // Añadir headers for (key, value) in APIConfig.commonHeaders { request.setValue(value, forHTTPHeaderField: key) } // Hacer request let (data, _) = try await URLSession.shared.data(for: request) // Parsear respuesta... print("Datos recibidos: \(data.count) bytes") } func downloadChapter(mangaSlug: String, chapterNumber: Int) async throws { // Usar endpoint predefinido let endpoint = APIConfig.Endpoints.download( mangaSlug: mangaSlug, chapterNumber: chapterNumber ) guard let url = URL(string: endpoint) else { return } var request = URLRequest(url: url) // Usar timeout más largo para descargas request.timeoutInterval = APIConfig.downloadTimeout for (key, value) in APIConfig.commonHeaders { request.setValue(value, forHTTPHeaderField: key) } // Hacer request let (data, response) = try await URLSession.shared.data(for: request) if let httpResponse = response as? HTTPURLResponse { print("Status: \(httpResponse.statusCode)") // Manejar errores específicos if httpResponse.statusCode != 200 { // Aquí podrías usar APIConfig.ErrorCodes si el backend // retorna códigos de error personalizados throw URLError(.badServerResponse) } } print("Descarga completada: \(data.count) bytes") } func checkServerHealth() async throws { // Usar endpoint de health check let endpoint = APIConfig.Endpoints.health() guard let url = URL(string: endpoint) else { return } var request = URLRequest(url: url) request.timeoutInterval = APIConfig.defaultTimeout let (_, response) = try await URLSession.shared.data(for: request) if let httpResponse = response as? HTTPURLResponse { print("Server health status: \(httpResponse.statusCode)") } } }