104 lines
		
	
	
		
			4.2 KiB
		
	
	
	
		
			Swift
		
	
	
	
	
	
			
		
		
	
	
			104 lines
		
	
	
		
			4.2 KiB
		
	
	
	
		
			Swift
		
	
	
	
	
	
| //
 | |
| //  ImageLoader.swift
 | |
| //  WatchRunner Watch App
 | |
| //
 | |
| //  Created by LittleSheep on 2025/10/29.
 | |
| //
 | |
| 
 | |
| import SwiftUI
 | |
| import Kingfisher
 | |
| import KingfisherWebP
 | |
| import Combine
 | |
| 
 | |
| // MARK: - Image Loader
 | |
| 
 | |
| @MainActor
 | |
| class ImageLoader: ObservableObject {
 | |
|     @Published var image: Image?
 | |
|     @Published var errorMessage: String?
 | |
|     @Published var isLoading = false
 | |
| 
 | |
|     private var dataTask: URLSessionDataTask?
 | |
|     private let session: URLSession
 | |
| 
 | |
|     init(session: URLSession = .shared) {
 | |
|         self.session = session
 | |
|     }
 | |
| 
 | |
|     deinit {
 | |
|         dataTask?.cancel()
 | |
|     }
 | |
| 
 | |
|     func loadImage(from initialUrl: URL, token: String) async {
 | |
|         isLoading = true
 | |
|         errorMessage = nil
 | |
|         image = nil
 | |
| 
 | |
|         do {
 | |
|             // First request with Authorization header
 | |
|             var request = URLRequest(url: initialUrl)
 | |
|             request.setValue("AtField \(token)", forHTTPHeaderField: "Authorization")
 | |
|             request.setValue("SolianWatch/1.0", forHTTPHeaderField: "User-Agent")
 | |
| 
 | |
|             let (data, response) = try await session.data(for: request)
 | |
| 
 | |
|             if let httpResponse = response as? HTTPURLResponse {
 | |
|                 if httpResponse.statusCode == 302, let redirectLocation = httpResponse.allHeaderFields["Location"] as? String, let redirectUrl = URL(string: redirectLocation) {
 | |
|                     print("[watchOS] Redirecting to: \(redirectUrl)")
 | |
|                     // Second request to the redirected URL (S3 signed URL) without Authorization header
 | |
|                     let (redirectData, _) = try await session.data(from: redirectUrl)
 | |
|                     if let uiImage = UIImage(data: redirectData) {
 | |
|                         self.image = Image(uiImage: uiImage)
 | |
|                         print("[watchOS] Image loaded successfully from redirect URL.")
 | |
|                     } else {
 | |
|                         // Try KingfisherWebP for WebP
 | |
|                         let processor = WebPProcessor.default // Correct usage
 | |
|                         if let kfImage = processor.process(item: .data(redirectData), options: KingfisherParsedOptionsInfo(
 | |
|                             [
 | |
|                                 .processor(processor),
 | |
|                                 .loadDiskFileSynchronously,
 | |
|                                 .cacheOriginalImage
 | |
|                             ]
 | |
|                         )) {
 | |
|                             self.image = Image(uiImage: kfImage)
 | |
|                             print("[watchOS] Image loaded successfully from redirect URL using KingfisherWebP.")
 | |
|                         } else {
 | |
|                             self.errorMessage = "Invalid image data from redirect (could not decode with KingfisherWebP)."
 | |
|                         }
 | |
|                     }
 | |
|                 } 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() {
 | |
|         dataTask?.cancel()
 | |
|     }
 | |
| }
 |