@@ -65,7 +65,7 @@ import SwiftUI
65
65
@available ( iOS 15 . 0 , macOS 12 . 0 , tvOS 15 . 0 , watchOS 8 . 0 , * )
66
66
public struct CachedAsyncImage < Content> : View where Content: View {
67
67
68
- @State private var phase : AsyncImagePhase = . empty
68
+ @State private var phase : AsyncImagePhase
69
69
70
70
private let url : URL ?
71
71
@@ -79,9 +79,7 @@ public struct CachedAsyncImage<Content>: View where Content: View {
79
79
80
80
public var body : some View {
81
81
content ( phase)
82
- . task ( id: url) {
83
- await load ( url: url)
84
- }
82
+ . task ( id: url, load)
85
83
}
86
84
87
85
/// Loads and displays an image from the specified URL.
@@ -201,61 +199,70 @@ public struct CachedAsyncImage<Content>: View where Content: View {
201
199
self . scale = scale
202
200
self . transaction = transaction
203
201
self . content = content
204
- loadFromCache ( url: url)
205
- }
206
-
207
- private func load( url: URL ? ) async {
208
- guard let url = url else { return }
209
- let request = URLRequest ( url: url)
210
- do {
211
- let ( data, _) = try await urlSession. data ( for: request)
212
- process ( data: data, animation: transaction. animation)
213
- } catch {
214
- withAnimation ( transaction. animation) {
215
- phase = . failure( error)
216
- }
202
+
203
+
204
+ if let image = cachedImage ( from: url, cache: urlCache) {
205
+ self . _phase = State ( wrappedValue: . success( image) )
206
+ } else {
207
+ self . _phase = State ( wrappedValue: . empty)
217
208
}
218
209
}
219
210
220
- private func loadFromCache( url: URL ? ) {
221
- guard let url = url else { return }
222
- let request = URLRequest ( url: url)
223
- guard let cachedResponse = urlSession. configuration. urlCache? . cachedResponse ( for: request) else { return }
224
- process ( data: cachedResponse. data)
225
- }
226
-
227
- private func process( data: Data , animation: Animation ? = nil ) {
211
+ @Sendable
212
+ private func load( ) async {
228
213
do {
229
- #if os(macOS)
230
- if let nsImage = NSImage ( data: data) {
231
- let image = Image ( nsImage: nsImage)
232
- withAnimation ( animation) {
233
- phase = . success( image)
234
- }
235
- } else {
236
- throw AsyncImage< Content> . LoadingError( )
237
- }
238
- #else
239
- if let uiImage = UIImage ( data: data) {
240
- let image = Image ( uiImage: uiImage)
241
- withAnimation ( animation) {
214
+ if let image = try await remoteImage ( from: url, session: urlSession) {
215
+ withAnimation ( transaction. animation) {
242
216
phase = . success( image)
243
217
}
244
218
} else {
245
219
throw AsyncImage< Content> . LoadingError( )
246
220
}
247
- #endif
248
221
} catch {
249
- withAnimation ( animation) {
222
+ withAnimation ( transaction . animation) {
250
223
phase = . failure( error)
251
224
}
252
225
}
253
226
}
254
227
}
255
228
229
+ // MARK: - LoadingError
230
+
256
231
@available ( iOS 15 . 0 , macOS 12 . 0 , tvOS 15 . 0 , watchOS 8 . 0 , * )
257
232
private extension AsyncImage {
258
233
259
234
struct LoadingError : Error {
260
235
}
261
236
}
237
+
238
+ // MARK: - Helpers
239
+
240
+ private func remoteImage( from url: URL ? , session: URLSession ) async throws -> Image ? {
241
+ guard let url = url else { return nil }
242
+ let request = URLRequest ( url: url)
243
+ let ( data, _) = try await session. data ( for: request)
244
+ return image ( from: data)
245
+ }
246
+
247
+ private func cachedImage( from url: URL ? , cache: URLCache ) -> Image ? {
248
+ guard let url = url else { return nil }
249
+ let request = URLRequest ( url: url)
250
+ guard let cachedResponse = cache. cachedResponse ( for: request) else { return nil }
251
+ return image ( from: cachedResponse. data)
252
+ }
253
+
254
+ private func image( from data: Data ) -> Image ? {
255
+ #if os(macOS)
256
+ if let nsImage = NSImage ( data: data) {
257
+ return Image ( nsImage: nsImage)
258
+ } else {
259
+ return nil
260
+ }
261
+ #else
262
+ if let uiImage = UIImage ( data: data) {
263
+ return Image ( uiImage: uiImage)
264
+ } else {
265
+ return nil
266
+ }
267
+ #endif
268
+ }
0 commit comments