[iOS] [SwiftUI] Image display from network URL (synchronous / asynchronous / cache / link / widget)

Display the image in the widgets, and tap the image to see ** View ** and ** ViewModel ** used for linking the app with the URL scheme.

ViewModel (Synchronous / Asynchronous / Cache of image data)

Does try? Data (contentsOf: URL) caches https://stackoverflow.com/a/57826757

URLImageViewModel.swift


import SwiftUI

final class URLImageViewModel: ObservableObject {
    
    @Published var downloadData: Data? = nil
    let url: String
    
    init(url: String, isSync: Bool = false) {
        self.url = url
        if isSync {
            self.downloadImageSync(url: self.url)
        } else {
            self.downloadImageAsync(url: self.url)
        }
    }
    
    func downloadImageAsync(url: String) {
        
        guard let imageURL = URL(string: url) else {
            return
        }
        
        let cache = URLCache.shared
        let request = URLRequest(url: URL(string: url)!, cachePolicy: URLRequest.CachePolicy.returnCacheDataElseLoad)
        if let data = cache.cachedResponse(for: request)?.data {
            self.downloadData = data
        }else {
            DispatchQueue.global().async {
                let data = try? Data(contentsOf: imageURL)
                DispatchQueue.main.async {
                    self.downloadData = data
                }
            }
        }
    }
    
    func downloadImageSync(url: String) {
        
        guard let imageURL = URL(string: url) else {
            return
        }
        
        let cache = URLCache.shared
        let request = URLRequest(url: URL(string: url)!, cachePolicy: URLRequest.CachePolicy.returnCacheDataElseLoad)
        if let data = cache.cachedResponse(for: request)?.data {
            self.downloadData = data
        }else {
            let data = try? Data(contentsOf: imageURL)
            self.downloadData = data
        }
    }
}

View (image display)

URLImageView.swift


import SwiftUI

struct URLImageView: View {
    
    @ObservedObject var viewModel: URLImageViewModel
    
    var body: some View {
        if let imageData = self.viewModel.downloadData {
            if let image = UIImage(data: imageData) {
                return Image(uiImage: image).resizable().scaledToFit()
            } else {
                return Image(uiImage: UIImage()).resizable().scaledToFit()
            }
        } else {
            return Image(uiImage: UIImage()).resizable().scaledToFit()
        }
    }
}

View (image display with link)

LinkURLImageView.swift


import SwiftUI

struct LinkURLImageView: View {
    let url: URL
    let imageUrlString: String
    let isSyncURLImage: Bool

    init(url: URL, imageUrlString: String, isSyncURLImage: Bool = false) {
        self.url = url
        self.imageUrlString = imageUrlString
        self.isSyncURLImage = isSyncURLImage
    }

    var body: some View {
        Link(destination: url) {
            let imageViewModel = URLImageViewModel(url: imageUrlString, isSync: isSyncURLImage)
            URLImageView(viewModel: imageViewModel)
        }
    }
}

Example of use

Asynchronous is not available for widgets, so use synchronous communication. (For asynchronous, specify false for isSyncURLImage)

WidgetEntryView.swift


import SwiftUI

    struct WidgetEntryView: View {
        var body: some View {
            LinkURLImageView(url: URL(string: "URL of the link you tapped (widgets)://)")!,
                             imageUrlString: "Image URL",
                             isSyncURLImage: true)
        }
}

Recommended Posts

[iOS] [SwiftUI] Image display from network URL (synchronous / asynchronous / cache / link / widget)