⚡ watchOS cache image
This commit is contained in:
		| @@ -182,6 +182,8 @@ | |||||||
| /* Begin PBXFileSystemSynchronizedRootGroup section */ | /* Begin PBXFileSystemSynchronizedRootGroup section */ | ||||||
| 		7310A7D52EB10962002C0FD3 /* WatchRunner Watch App */ = { | 		7310A7D52EB10962002C0FD3 /* WatchRunner Watch App */ = { | ||||||
| 			isa = PBXFileSystemSynchronizedRootGroup; | 			isa = PBXFileSystemSynchronizedRootGroup; | ||||||
|  | 			exceptions = ( | ||||||
|  | 			); | ||||||
| 			path = "WatchRunner Watch App"; | 			path = "WatchRunner Watch App"; | ||||||
| 			sourceTree = "<group>"; | 			sourceTree = "<group>"; | ||||||
| 		}; | 		}; | ||||||
| @@ -669,14 +671,10 @@ | |||||||
| 			inputFileListPaths = ( | 			inputFileListPaths = ( | ||||||
| 				"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-input-files.xcfilelist", | 				"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-input-files.xcfilelist", | ||||||
| 			); | 			); | ||||||
| 			inputPaths = ( |  | ||||||
| 			); |  | ||||||
| 			name = "[CP] Copy Pods Resources"; | 			name = "[CP] Copy Pods Resources"; | ||||||
| 			outputFileListPaths = ( | 			outputFileListPaths = ( | ||||||
| 				"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-output-files.xcfilelist", | 				"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-output-files.xcfilelist", | ||||||
| 			); | 			); | ||||||
| 			outputPaths = ( |  | ||||||
| 			); |  | ||||||
| 			runOnlyForDeploymentPostprocessing = 0; | 			runOnlyForDeploymentPostprocessing = 0; | ||||||
| 			shellPath = /bin/sh; | 			shellPath = /bin/sh; | ||||||
| 			shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n"; | 			shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n"; | ||||||
| @@ -734,14 +732,10 @@ | |||||||
| 			inputFileListPaths = ( | 			inputFileListPaths = ( | ||||||
| 				"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", | 				"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", | ||||||
| 			); | 			); | ||||||
| 			inputPaths = ( |  | ||||||
| 			); |  | ||||||
| 			name = "[CP] Embed Pods Frameworks"; | 			name = "[CP] Embed Pods Frameworks"; | ||||||
| 			outputFileListPaths = ( | 			outputFileListPaths = ( | ||||||
| 				"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", | 				"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", | ||||||
| 			); | 			); | ||||||
| 			outputPaths = ( |  | ||||||
| 			); |  | ||||||
| 			runOnlyForDeploymentPostprocessing = 0; | 			runOnlyForDeploymentPostprocessing = 0; | ||||||
| 			shellPath = /bin/sh; | 			shellPath = /bin/sh; | ||||||
| 			shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; | 			shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; | ||||||
| @@ -770,14 +764,10 @@ | |||||||
| 			inputFileListPaths = ( | 			inputFileListPaths = ( | ||||||
| 				"${PODS_ROOT}/Target Support Files/Pods-WatchRunner Watch App/Pods-WatchRunner Watch App-frameworks-${CONFIGURATION}-input-files.xcfilelist", | 				"${PODS_ROOT}/Target Support Files/Pods-WatchRunner Watch App/Pods-WatchRunner Watch App-frameworks-${CONFIGURATION}-input-files.xcfilelist", | ||||||
| 			); | 			); | ||||||
| 			inputPaths = ( |  | ||||||
| 			); |  | ||||||
| 			name = "[CP] Embed Pods Frameworks"; | 			name = "[CP] Embed Pods Frameworks"; | ||||||
| 			outputFileListPaths = ( | 			outputFileListPaths = ( | ||||||
| 				"${PODS_ROOT}/Target Support Files/Pods-WatchRunner Watch App/Pods-WatchRunner Watch App-frameworks-${CONFIGURATION}-output-files.xcfilelist", | 				"${PODS_ROOT}/Target Support Files/Pods-WatchRunner Watch App/Pods-WatchRunner Watch App-frameworks-${CONFIGURATION}-output-files.xcfilelist", | ||||||
| 			); | 			); | ||||||
| 			outputPaths = ( |  | ||||||
| 			); |  | ||||||
| 			runOnlyForDeploymentPostprocessing = 0; | 			runOnlyForDeploymentPostprocessing = 0; | ||||||
| 			shellPath = /bin/sh; | 			shellPath = /bin/sh; | ||||||
| 			shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-WatchRunner Watch App/Pods-WatchRunner Watch App-frameworks.sh\"\n"; | 			shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-WatchRunner Watch App/Pods-WatchRunner Watch App-frameworks.sh\"\n"; | ||||||
|   | |||||||
| @@ -18,15 +18,12 @@ class ImageLoader: ObservableObject { | |||||||
|     @Published var errorMessage: String? |     @Published var errorMessage: String? | ||||||
|     @Published var isLoading = false |     @Published var isLoading = false | ||||||
|  |  | ||||||
|     private var dataTask: URLSessionDataTask? |     private var currentTask: DownloadTask? | ||||||
|     private let session: URLSession |  | ||||||
|  |  | ||||||
|     init(session: URLSession = .shared) { |     init() {} | ||||||
|         self.session = session |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     deinit { |     deinit { | ||||||
|         dataTask?.cancel() |         currentTask?.cancel() | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     func loadImage(from initialUrl: URL, token: String) async { |     func loadImage(from initialUrl: URL, token: String) async { | ||||||
| @@ -34,70 +31,67 @@ class ImageLoader: ObservableObject { | |||||||
|         errorMessage = nil |         errorMessage = nil | ||||||
|         image = nil |         image = nil | ||||||
|  |  | ||||||
|         do { |         // Create request modifier for authorization | ||||||
|             // First request with Authorization header |         let modifier = AnyModifier { request in | ||||||
|             var request = URLRequest(url: initialUrl) |             var r = request | ||||||
|             request.setValue("AtField \(token)", forHTTPHeaderField: "Authorization") |             r.setValue("AtField \(token)", forHTTPHeaderField: "Authorization") | ||||||
|             request.setValue("SolianWatch/1.0", forHTTPHeaderField: "User-Agent") |             r.setValue("SolianWatch/1.0", forHTTPHeaderField: "User-Agent") | ||||||
|  |             return r | ||||||
|  |         } | ||||||
|  |  | ||||||
|             let (data, response) = try await session.data(for: request) |         // Use WebP processor as default since the app seems to handle WebP images | ||||||
|  |         let processor = WebPProcessor.default | ||||||
|  |  | ||||||
|             if let httpResponse = response as? HTTPURLResponse { |         // Use KingfisherManager to retrieve image with caching | ||||||
|                 if httpResponse.statusCode == 302, let redirectLocation = httpResponse.allHeaderFields["Location"] as? String, let redirectUrl = URL(string: redirectLocation) { |         currentTask = KingfisherManager.shared.retrieveImage( | ||||||
|                     print("[watchOS] Redirecting to: \(redirectUrl)") |             with: initialUrl, | ||||||
|                     // Second request to the redirected URL (S3 signed URL) without Authorization header |             options: [ | ||||||
|                     let (redirectData, _) = try await session.data(from: redirectUrl) |                 .requestModifier(modifier), | ||||||
|                     if let uiImage = UIImage(data: redirectData) { |                 .processor(processor), | ||||||
|                         self.image = Image(uiImage: uiImage) |                 .cacheOriginalImage, // Cache the original image data | ||||||
|                         print("[watchOS] Image loaded successfully from redirect URL.") |                 .loadDiskFileSynchronously // Load from disk cache synchronously if available | ||||||
|                     } else { |             ] | ||||||
|                         // Try KingfisherWebP for WebP |         ) { [weak self] result in | ||||||
|                         let processor = WebPProcessor.default // Correct usage |             guard let self = self else { return } | ||||||
|                         if let kfImage = processor.process(item: .data(redirectData), options: KingfisherParsedOptionsInfo( |  | ||||||
|                             [ |             Task { @MainActor in | ||||||
|                                 .processor(processor), |                 switch result { | ||||||
|                                 .loadDiskFileSynchronously, |                 case .success(let value): | ||||||
|                                 .cacheOriginalImage |                     self.image = Image(uiImage: value.image) | ||||||
|                             ] |                     print("[watchOS] Image loaded successfully from \(value.cacheType == .none ? "network" : "cache (\(value.cacheType))").") | ||||||
|                         )) { |                     self.isLoading = false | ||||||
|                             self.image = Image(uiImage: kfImage) |                 case .failure(let error): | ||||||
|                             print("[watchOS] Image loaded successfully from redirect URL using KingfisherWebP.") |                     // If WebP processor fails (likely due to format), try with default processor | ||||||
|                         } else { |                     let defaultProcessor = DefaultImageProcessor.default | ||||||
|                             self.errorMessage = "Invalid image data from redirect (could not decode with KingfisherWebP)." |                     self.currentTask = KingfisherManager.shared.retrieveImage( | ||||||
|  |                         with: initialUrl, | ||||||
|  |                         options: [ | ||||||
|  |                             .requestModifier(modifier), | ||||||
|  |                             .processor(defaultProcessor), | ||||||
|  |                             .cacheOriginalImage, | ||||||
|  |                             .loadDiskFileSynchronously | ||||||
|  |                         ] | ||||||
|  |                     ) { [weak self] fallbackResult in | ||||||
|  |                         guard let self = self else { return } | ||||||
|  |  | ||||||
|  |                         Task { @MainActor in | ||||||
|  |                             switch fallbackResult { | ||||||
|  |                             case .success(let value): | ||||||
|  |                                 self.image = Image(uiImage: value.image) | ||||||
|  |                                 print("[watchOS] Image loaded successfully from \(value.cacheType == .none ? "network" : "cache (\(value.cacheType))") using fallback processor.") | ||||||
|  |                             case .failure(let fallbackError): | ||||||
|  |                                 self.errorMessage = fallbackError.localizedDescription | ||||||
|  |                                 print("[watchOS] Image loading failed: \(fallbackError.localizedDescription)") | ||||||
|  |                             } | ||||||
|  |                             self.isLoading = false | ||||||
|                         } |                         } | ||||||
|                     } |                     } | ||||||
|                 } else if httpResponse.statusCode == 200 { |  | ||||||
|                     if let uiImage = UIImage(data: data) { |  | ||||||
|                         self.image = Image(uiImage: uiImage) |  | ||||||
|                         print("[watchOS] Image loaded successfully from initial URL.") |  | ||||||
|                     } else { |  | ||||||
|                         // Try KingfisherWebP for WebP |  | ||||||
|                         let processor = WebPProcessor.default // Correct usage |  | ||||||
|                         if let kfImage = processor.process(item: .data(data), options: KingfisherParsedOptionsInfo( |  | ||||||
|                             [ |  | ||||||
|                                 .processor(processor), |  | ||||||
|                                 .loadDiskFileSynchronously, |  | ||||||
|                                 .cacheOriginalImage |  | ||||||
|                             ] |  | ||||||
|                         )) { |  | ||||||
|                             self.image = Image(uiImage: kfImage) |  | ||||||
|                             print("[watchOS] Image loaded successfully from initial URL using KingfisherWebP.") |  | ||||||
|                         } else { |  | ||||||
|                             self.errorMessage = "Invalid image data (could not decode with KingfisherWebP)." |  | ||||||
|                         } |  | ||||||
|                     } |  | ||||||
|                 } else { |  | ||||||
|                     self.errorMessage = "HTTP Status Code: \(httpResponse.statusCode)" |  | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|         } catch { |  | ||||||
|             self.errorMessage = error.localizedDescription |  | ||||||
|             print("[watchOS] Image loading failed: \(error.localizedDescription)") |  | ||||||
|         } |         } | ||||||
|         isLoading = false |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     func cancel() { |     func cancel() { | ||||||
|         dataTask?.cancel() |         currentTask?.cancel() | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user