添加 Solian for Apple Watch #8
@@ -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) {
|
|
||||||
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),
|
.processor(processor),
|
||||||
.loadDiskFileSynchronously,
|
.cacheOriginalImage, // Cache the original image data
|
||||||
.cacheOriginalImage
|
.loadDiskFileSynchronously // Load from disk cache synchronously if available
|
||||||
]
|
]
|
||||||
)) {
|
) { [weak self] result in
|
||||||
self.image = Image(uiImage: kfImage)
|
guard let self = self else { return }
|
||||||
print("[watchOS] Image loaded successfully from redirect URL using KingfisherWebP.")
|
|
||||||
} else {
|
Task { @MainActor in
|
||||||
self.errorMessage = "Invalid image data from redirect (could not decode with KingfisherWebP)."
|
switch result {
|
||||||
}
|
case .success(let value):
|
||||||
}
|
self.image = Image(uiImage: value.image)
|
||||||
} else if httpResponse.statusCode == 200 {
|
print("[watchOS] Image loaded successfully from \(value.cacheType == .none ? "network" : "cache (\(value.cacheType))").")
|
||||||
if let uiImage = UIImage(data: data) {
|
self.isLoading = false
|
||||||
self.image = Image(uiImage: uiImage)
|
case .failure(let error):
|
||||||
print("[watchOS] Image loaded successfully from initial URL.")
|
// If WebP processor fails (likely due to format), try with default processor
|
||||||
} else {
|
let defaultProcessor = DefaultImageProcessor.default
|
||||||
// Try KingfisherWebP for WebP
|
self.currentTask = KingfisherManager.shared.retrieveImage(
|
||||||
let processor = WebPProcessor.default // Correct usage
|
with: initialUrl,
|
||||||
if let kfImage = processor.process(item: .data(data), options: KingfisherParsedOptionsInfo(
|
options: [
|
||||||
[
|
.requestModifier(modifier),
|
||||||
.processor(processor),
|
.processor(defaultProcessor),
|
||||||
.loadDiskFileSynchronously,
|
.cacheOriginalImage,
|
||||||
.cacheOriginalImage
|
.loadDiskFileSynchronously
|
||||||
]
|
]
|
||||||
)) {
|
) { [weak self] fallbackResult in
|
||||||
self.image = Image(uiImage: kfImage)
|
guard let self = self else { return }
|
||||||
print("[watchOS] Image loaded successfully from initial URL using KingfisherWebP.")
|
|
||||||
} else {
|
Task { @MainActor in
|
||||||
self.errorMessage = "Invalid image data (could not decode with KingfisherWebP)."
|
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 {
|
|
||||||
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