# Quick Implementation Guide - MangaReader Optimizations ## 📂 Archivos Creados ``` ios-app/Sources/Services/ ├── ManhwaWebScraperOptimized.swift # Scraper con cache inteligente ├── StorageServiceOptimized.swift # Storage con compresión y thumbnails ├── ImageCache.swift # Sistema de cache de imágenes └── CacheManager.swift # Gerente central de cache ios-app/Sources/Views/ └── ReaderViewOptimized.swift # Reader optimizado OPTIMIZATION_SUMMARY.md # Documentación completa IMPLEMENTATION_GUIDE.md # Este archivo ``` --- ## 🔄 Migración Rápida ### Opción 1: Reemplazo Directo (Recomendado para Testing) ```swift // 1. En tus ViewModels, cambia: private let scraper = ManhwaWebScraper.shared // Por: private let scraper = ManhwaWebScraperOptimized.shared // 2. En tus Views, cambia: private let storage = StorageService.shared // Por: private let storage = StorageServiceOptimized.shared // 3. En ReaderView: // Reemplaza completamente por ReaderViewOptimized ``` ### Opción 2: Migración Gradual (Más Segura) ```swift // Usa alias de tipo para migrar gradualmente typealias Scraper = ManhwaWebScraperOptimized typealias Storage = StorageServiceOptimized // Código existente funciona sin cambios private let scraper = Scraper.shared private let storage = Storage.shared ``` --- ## 🔧 Configuración Inicial ### 1. Inicializar CacheManager en AppDelegate ```swift import UIKit @main class AppDelegate: UIResponder, UIApplicationDelegate { func application( _ application: UIApplication, didFinishLaunchingWithOptions ... ) -> Bool { // Inicializar cache manager let cacheManager = CacheManager.shared #if DEBUG // Imprimir reporte inicial en debug cacheManager.printCacheReport() #endif return true } // Monitoring de background func applicationDidEnterBackground(_ application: UIApplication) { // El CacheManager ya maneja esto automáticamente // pero puedes agregar lógica adicional aquí } // Monitoring de memory warnings func applicationDidReceiveMemoryWarning(_ application: UIApplication) { // CacheManager ya responde automáticamente print("⚠️ Memory warning received") } } ``` ### 2. Configurar Ajustes de Cache ```swift // En algún lugar de configuración (ej: SettingsViewModel) struct CacheSettings { // Ajusta según tus necesidades // Tamaño máximo de cache (en bytes) static let maxImageCacheSize: Int64 = 500 * 1024 * 1024 // 500 MB static let maxMetadataCacheSize: Int64 = 50 * 1024 * 1024 // 50 MB // Tiempos de expiración (en segundos) static let htmlCacheDuration: TimeInterval = 1800 // 30 min static let imageCacheDuration: TimeInterval = 7 * 86400 // 7 días static let thumbnailCacheDuration: TimeInterval = 30 * 86400 // 30 días // Preloading static let preloadAdjacentPages: Int = 2 // 2 páginas antes/después static let enablePreloading: Bool = true } ``` --- ## 📊 Monitoreo y Debugging ### Ver Estadísticas en Debug ```swift #if DEBUG // Agregar botón de debug en settings struct DebugSettingsView: View { var body: some View { VStack { Button("Print Cache Report") { CacheManager.shared.printCacheReport() } Button("Print Image Statistics") { ImageCache.shared.printStatistics() } Button("Clear All Cache") { CacheManager.shared.clearAllCache() } Button("Simulate Memory Warning") { NotificationCenter.default.post( name: UIApplication.didReceiveMemoryWarningNotification, object: nil ) } } } } #endif ``` ### Instruments Configuration ```bash # Para perfilado de memoria 1. Xcode → Product → Profile 2. Seleccionar "Allocations" 3. Monitorear: - Overall memory usage - Anonymous VM - Image cache size # Para perfilado de tiempo 1. Seleccionar "Time Profiler" 2. Buscar funciones lentas 3. Verificar que cache esté funcionando ``` --- ## 🧪 Testing Checklist ### Tests Funcionales ```swift class CachePerformanceTests: XCTestCase { func testScraperCache() async throws { let scraper = ManhwaWebScraperOptimized.shared // Primer scraping (debe ser lento) let start1 = Date() _ = try await scraper.scrapeChapters(mangaSlug: "test-manga") let time1 = Date().timeIntervalSince(start1) // Segundo scraping (debe ser rápido por cache) let start2 = Date() _ = try await scraper.scrapeChapters(mangaSlug: "test-manga") let time2 = Date().timeIntervalSince(start2) // El segundo debe ser >10x más rápido XCTAssertLessThan(time2, time1 / 10) } func testImageCompression() { let storage = StorageServiceOptimized.shared // Crear imagen de prueba grande (4 MB) let largeImage = createTestImage(size: CGSize(width: 2000, height: 3000)) let expectation = XCTestExpectation(description: "Compress and save") Task { let url = try await storage.saveImage( largeImage, mangaSlug: "test", chapterNumber: 1, pageIndex: 0 ) // Verificar que archivo resultante es < 2 MB let attributes = try FileManager.default.attributesOfItem(atPath: url.path) let fileSize = attributes[.size] as! Int64 XCTAssertLessThan(fileSize, 2 * 1024 * 1024) expectation.fulfill() } wait(for: [expectation], timeout: 5.0) } func testPreloading() { let viewModel = ReaderViewModelOptimized( manga: testManga, chapter: testChapter ) // Cargar páginas let expectation = XCTestExpectation(description: "Load pages") Task { await viewModel.loadPages() expectation.fulfill() } wait(for: [expectation], timeout: 10.0) // Simular navegación a página 5 viewModel.currentPage = 5 // Verificar que páginas 3, 4, 6, 7 están en cache let imageCache = ImageCache.shared XCTAssertNotNil(imageCache.image(for: viewModel.pages[3].url)) XCTAssertNotNil(imageCache.image(for: viewModel.pages[4].url)) XCTAssertNotNil(imageCache.image(for: viewModel.pages[6].url)) XCTAssertNotNil(imageCache.image(for: viewModel.pages[7].url)) } } ``` ### Tests de Memoria ```swift class MemoryTests: XCTestCase { func testMemoryUnderLoad() { let viewModel = ReaderViewModelOptimized( manga: largeManga, // 100 páginas chapter: largeChapter ) let memoryBefore = getMemoryUsage() let expectation = XCTestExpectation(description: "Load all pages") Task { await viewModel.loadPages() // Navegar por todas las páginas for i in 0.. UInt64 { var info = mach_task_basic_info() var count = mach_msg_type_number_t(MemoryLayout.size)/4 let result = withUnsafeMutablePointer(to: &info) { $0.withMemoryRebound(to: integer_t.self, capacity: 1) { task_info(mach_task_self_, task_flavor_t(MACH_TASK_BASIC_INFO), $0, &count) } } return result == KERN_SUCCESS ? info.resident_size : 0 } } ``` --- ## 🎯 Optimizaciones por Categoría ### Scraper Optimizations | Optimización | Archivo | Líneas Clave | |--------------|---------|--------------| | WKWebView reuse | `ManhwaWebScraperOptimized.swift` | 30-45 | | HTML cache | `ManhwaWebScraperOptimized.swift` | 48-62 | | Precompiled JS | `ManhwaWebScraperOptimized.swift` | 67-122 | | Adaptive timeout | `ManhwaWebScraperOptimized.swift` | 127-165 | | Concurrency control | `ManhwaWebScraperOptimized.swift` | 170-195 | ### Storage Optimizations | Optimización | Archivo | Líneas Clave | |--------------|---------|--------------| | Adaptive compression | `StorageServiceOptimized.swift` | 28-52 | | Thumbnail system | `StorageServiceOptimized.swift` | 57-85 | | Lazy loading | `StorageServiceOptimized.swift` | 478-488 | | Auto cleanup | `StorageServiceOptimized.swift` | 330-380 | | Batch operations | `StorageServiceOptimized.swift` | 220-245 | ### Reader Optimizations | Optimización | Archivo | Líneas Clave | |--------------|---------|--------------| | NSCache | `ImageCache.swift` | 22-45 | | Preloading | `ReaderViewOptimized.swift` | 195-215 | | Memory management | `ReaderViewOptimized.swift` | 420-445 | | Debouncing | `ReaderViewOptimized.swift` | 505-525 | --- ## 🔍 Troubleshooting ### Problema: Cache no funciona ```swift // Verificar que el singleton se esté usando correctamente let scraper = ManhwaWebScraperOptimized.shared // NO: let scraper = ManhwaWebScraperOptimized() // Verificar que los métodos tengan @MainActor si es necesario @MainActor func loadChapters() async throws { // código } ``` ### Problema: Memoria sigue alta ```swift // 1. Verificar que se estén liberando referencias deinit { progressSaveTimer?.invalidate() NotificationCenter.default.removeObserver(self) } // 2. Usizar weak en closures Task { [weak self] in await self?.loadPages() } // 3. Verificar en Instruments // - Revisar "Anonymous VM" (debe ser bajo) // - Buscar "Malloc" leaks ``` ### Problema: Preloading no funciona ```swift // Verificar que esté habilitado viewModel.enablePreloading = true // Debe ser true // Verificar que onAppear se llame PageView(page: page) .onAppear { print("Page \(page.index) appeared") // Debug viewModel.preloadAdjacentPages(...) } // Verificar priority queue Task(priority: .utility) { // Debe usar .utility, no .background } ``` ### Problema: Thumbnails no se generan ```swift // Verificar directorio de thumbnails let thumbDir = storage.getThumbnailDirectory(...) print("Thumb dir: \(thumbDir.path)") // Verificar que se cree el directorio try? FileManager.default.createDirectory( at: thumbDir, withIntermediateDirectories: true ) // Verificar que se guarde correctamente try? thumbData.write(to: thumbnailURL) ``` --- ## 📈 Métricas de Éxito ### Objetivos de Rendimiento ```swift struct PerformanceTargets { // Tiempos de carga static let maxChapterLoadTime: TimeInterval = 2.0 // segundos static let maxPageLoadTime: TimeInterval = 0.5 // segundos (con cache) static let maxAppLaunchTime: TimeInterval = 1.0 // segundos // Memoria static let maxReaderMemory: UInt64 = 120 * 1024 * 1024 // 120 MB static let maxScraperMemory: UInt64 = 50 * 1024 * 1024 // 50 MB // Cache static let minCacheHitRate: Double = 0.80 // 80% static let maxCacheSize: Int64 = 500 * 1024 * 1024 // 500 MB } // Función de verificación func verifyPerformanceTargets() { let cacheStats = ImageCache.shared.getCacheStatistics() let hitRate = cacheStats.hitRate XCTAssertGreaterThan(hitRate, PerformanceTargets.minCacheHitRate, "Cache hit rate \(hitRate) below target") // ... más verificaciones } ``` --- ## 🚀 Próximos Pasos ### Implementación Inmediata 1. **Backup del código existente** ```bash cp -r ios-app/Sources ios-app/Sources.backup ``` 2. **Reemplazar archivos uno por uno** - Empezar con ManhwaWebScraperOptimized - Probar exhaustivamente - Continuar con StorageServiceOptimized - etc. 3. **Testing completo** - Unit tests - Integration tests - Manual testing ### Optimizaciones Futuras (Opcionales) 1. **Prefetching Predictivo** ```swift // Usar ML para predecir próximo capítulo func predictNextChapter(userHistory: [Chapter]) -> Chapter? { // Implementación básica de ML } ``` 2. **Compresión HEIC** ```swift // Usar HEIC en lugar de JPEG (50% más eficiente) if #available(iOS 11.0, *) { let heicData = image.heicData() } ``` 3. **Progressive Image Loading** ```swift // Cargar versión baja resolución primero // Luego reemplazar con alta resolución ``` 4. **Background Sync** ```swift // Sincronizar en background usando BGTaskScheduler func scheduleBackgroundSync() { let request = BGAppRefreshTaskRequest(...) try? BGTaskScheduler.shared.submit(request) } ``` --- ## 💡 Tips y Best Practices ### DO ✅ ```swift // 1. Usar singletons para caches static let shared = ImageCache() // 2. Usar colas para I/O private let ioQueue = DispatchQueue(label: "...", qos: .utility) // 3. Weak references en closures Task { [weak self] in await self?.loadData() } // 4. Responder a memory warnings @objc func handleMemoryWarning() { cache.removeAllObjects() } // 5. Debouncing de operaciones frecuentes func saveDebounced() { timer?.invalidate() timer = Timer.scheduledTimer(...) } ``` ### DON'T ❌ ```swift // 1. NO crear múltiples instancias de cache // let cache1 = ImageCache() // let cache2 = ImageCache() // ❌ // 2. NO hacer I/O en main thread // let data = try Data(contentsOf: url) // ❌ Bloquea // 3. NO olvidar weak en closures // Task { // self.doSomething() // ❌ Memory leak // } // 4. NO ignorar memory warnings // @objc func handleMemoryWarning() { // // ❌ No hacer nada // } // 5. NO guardar en cada cambio // func onChange() { // saveToDisk() // ❌ Demasiado frecuente // } ``` --- ## 📞 Soporte Si encuentras problemas: 1. Revisa `OPTIMIZATION_SUMMARY.md` para detalles técnicos 2. Usa los tests de ejemplo como guía 3. Habilita logging debug en development 4. Usa Instruments para perfilado --- **Happy Optimizing! 🚀**