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()
 | 
						|
    }
 | 
						|
}
 |