ウィジェット(widgets)内に画像を表示し、画像タップでURLスキームによるアプリ連携のために使用したViewとViewModelになります。
##ViewModel(画像データの同期・非同期・キャッシュ)
Does try? Data(contentsOf: URL) でキャッシュしてます
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(画像表示)
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(リンク付き画像表示)
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)
}
}
}
##使用例
ウィジェット(widgets)では非同期が使用できないため、同期通信を使用します。
(非同期はisSyncURLImageにfalseを指定)
WidgetEntryView.swift
import SwiftUI
struct WidgetEntryView: View {
var body: some View {
LinkURLImageView(url: URL(string: "タップ先のリンクのURL(widgets://)")!,
imageUrlString: "画像のURL",
isSyncURLImage: true)
}
}