SwiftUI’s AsyncImage cache policy isn’t adequate for our use, which means we’re hitting the network too much. Let’s write our own CachedAsyncImage view that will…
- Fetch images given a url
- Save images to the documents directory
- Load images from the documents directory
- Act as a drop in replacement for AsyncImage
Goals
- Dramatically decrease network traffic
- Local image storage for future features
Problems
There are a few issues with this implementation as is that we’ll address in later installments.
- Memory usage goes up with each image displayed
Steps
- Set minimum
PodImageView
height to300
Get documents directory asURL
UIImage extension save as.jpg
- Create minimum
CachedAsyncImage
view GET image byURL
Load image from documents directory- Logic for CachedAsyncImage
body
- Preview for CachedAsyncImage
Network
static let shared = Network()
CachedAsyncImage
struct CachedAsyncImage<Content>: View where Content: View {
private let url: URL
private let scale: CGFloat
private let transaction: Transaction
private let content: (AsyncImagePhase) -> Content
@State private var image: UIImage?
init(
url: URL,
scale: CGFloat = 1.0,
transaction: Transaction = Transaction(),
@ViewBuilder content: @escaping (AsyncImagePhase) -> Content
) {
self.url = url
self.scale = scale
self.transaction = transaction
self.content = content
}
var body: some View {
if let cached = FileManager.loadImage(name: url.lastPathComponent) ?? image {
content(.success(Image(uiImage: cached)))
.animation(transaction.animation, value: cached)
} else {
content(.empty).task {
image = await Network.shared.getImage(url: url)
image?.saveJPG(name: url.lastPathComponent)
}
}
}
}
8 CachedAsyncImage_Previews
struct CachedAsyncImage_Previews: PreviewProvider {
static let url = URL(string: "https://apod.nasa.gov/apod/image/2112/JwstLaunch_Arianespace_1080.jpg")!
static var previews: some View {
CachedAsyncImage(url: url) { phase in
switch phase {
case .empty:
ProgressView()
case .success(let image):
image
case .failure(let error):
ErrorView(description: error.localizedDescription)
[@unknown](https://micro.blog/unknown) default:
fatalError()
}
}
}
}