5
4

More than 3 years have passed since last update.

[iOS][SwiftUI]ネットワークのURLから画像表示(同期・非同期・キャッシュ・リンク・ウィジェット)

Posted at

ウィジェット(widgets)内に画像を表示し、画像タップでURLスキームによるアプリ連携のために使用したViewViewModelになります。

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)
        }
}
5
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
4