Here's a class that I wrote, works well in ios 16.
It loads the image data and saves it to disk. If the image is already stored on disk it loads the image from disk instead.
Usage:
ImageFromWebOrDisk(baseURL:"https://www.example.com/images/", filename:"myimage.jpg")
.mask(
RoundedRectangle(cornerRadius: 16)
)
Class:
import SwiftUI
struct ImageFromWebOrDisk: View {
var baseURL:String
var filename:String
@State var image: UIImage?
var body:some View {
VStack {
if let image = image {
Image(uiImage: image)
.resizable()
.aspectRatio(contentMode: .fit)
} else {
ProgressView()
//Text("Loading image...")
}
}
.onAppear {
loadImage()
}/*
.onChange(of: filename) { _ in
print("load image: \(filename)")
DispatchQueue.main.asyncAfter(deadline: .now() + 0.01) {
self.loadImage()
}
}*/
}
func loadImage() {
let fileURL = getDocumentsDirectory().appendingPathComponent(filename)
if FileManager.default.fileExists(atPath: fileURL.path) {
// Load image from disk
if let imageData = try? Data(contentsOf: fileURL),
let loadedImage = UIImage(data: imageData) {
image = loadedImage
print("Loaded image from disk: \(fileURL)")
return
}
}
// Image not found on disk, fetch from URL
loadImageFromURL(urlString: "\(baseURL)\(filename)") { loadedImage in
guard let loadedImage = loadedImage else {
return
}
print("Loaded from URL: \(fileURL)")
image = loadedImage
saveImageToDisk(image: loadedImage, filename: filename)
}
}
func loadImageFromURL(urlString: String, completion: @escaping (UIImage?) -> Void) {
guard let url = URL(string: urlString) else {
completion(nil)
return
}
URLSession.shared.dataTask(with: url) { data, _, error in
guard let data = data, error == nil else {
completion(nil)
return
}
let loadedImage = UIImage(data: data)
completion(loadedImage)
}.resume()
}
func saveImageToDisk(image: UIImage?, filename:String) {
guard let image = image,
let imageData = image.jpegData(compressionQuality: 0.9) else {
return
}
let fileURL = getDocumentsDirectory().appendingPathComponent(filename)
do {
try imageData.write(to: fileURL)
print("Image saved to disk: \(fileURL)")
} catch {
print("Failed to save image to disk: \(error)")
}
}
func getDocumentsDirectory() -> URL {
let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
return paths[0]
}
}
UPDATE REQUEST:
If someone can make it CHANGE image if url changes, that'd be great.
Right now how I solve it is the following:
if showPhotoWithURL != "" {
ImageFromWebOrDisk(baseURL:GlobalSettings.avatarImagesBase, filename:"\(showPhotoWithURL).jpg")
.frame(width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.width)
} else {
ProgressView()
.frame(width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.width)
}
And the button action:
Button(action: {
showPhotoWithURL = ""
// to reload the image
DispatchQueue.main.asyncAfter(deadline: .now() + 0.01) {
self.showPhotoWithURL = message.answer.image
}
}