WWDC22 SALE: Save 50% on all my Swift books and bundles! >>

SOLVED: loading images

Forums > SwiftUI

I'm writing an app that reads a JSON data file. One of the fields in the JSON file is a URL to an image. When I display parts of the JSON file, I would also like to display the image .

Whats the best approach to do this?

1      

What is an example a the URL?

1      

Save 50% in my Black Friday sale.

SAVE 50% To celebrate WWDC22, all our books and bundles are half price, so you can take your Swift knowledge further without spending big! Get the Swift Power Pack to build your iOS career faster, get the Swift Platform Pack to builds apps for macOS, watchOS, and beyond, or get the Swift Plus Pack to learn advanced design patterns, testing skills, and more.

Save 50% on all our books and bundles!

Sponsor Hacking with Swift and reach the world's largest Swift community!

Make a file with this code from Hacking with Swift plus

import SwiftUI

struct RemoteImage: View {
    private enum LoadState {
        case loading, success, failure
    }

    private class Loader: ObservableObject {
        var data = Data()
        var state = LoadState.loading

        init(url: String) {
            guard let parsedURL = URL(string: url) else {
                fatalError("Invalid URL: \(url)")
            }

            URLSession.shared.dataTask(with: parsedURL) { data, response, error in
                if let data = data, data.count > 0 {
                    self.data = data
                    self.state = .success
                } else {
                    self.state = .failure
                }

                DispatchQueue.main.async {
                    self.objectWillChange.send()
                }
            }.resume()
        }
    }

    @StateObject private var loader: Loader
    var loading: Image
    var failure: Image

    var body: some View {
        selectImage()
            .resizable()
    }

    init(url: String, loading: Image = Image(systemName: "photo"), failure: Image = Image(systemName: "multiply.circle")) {
        _loader = StateObject(wrappedValue: Loader(url: url))
        self.loading = loading
        self.failure = failure
    }

    private func selectImage() -> Image {
        switch loader.state {
        case .loading:
            return loading
        case .failure:
            return failure
        default:
            if let image = UIImage(data: loader.data) {
                return Image(uiImage: image)
            } else {
                return failure
            }
        }
    }
}

then use in your view

struct ContentView: View {
    let jsonURL = "https://cf.geekdo-images.com/thumb/img/sD_qvrzIbvfobJj0ZDAaq-TnQPs=/fit-in/200x150/pic2649952.jpg"

    var body: some View {
        RemoteImage(url: jsonURL)
            .aspectRatio(contentMode: .fit)
            .frame(width: 200)
    }
}

Hope that helps

3      

thanks Nigel i will give that a try.

1      

Didn't seem to like @StateObject, but I just replaced that with @ObservedObject and the code seems to work. Thanks aain.

1      

The @StateObject is for Xcode 12

1      

This works, but there's no caching. Has anyone implemented a caching solution they'd like to share? :)

1      

Hi, thank you for this, it works good. But do you have a trick to reload if needed ? For now i reload the parents view and it works but is there a better way ?

2      

This works perfectly when given a URL upon initialization. However, if my URL is part of a @State or @ObservedObject that changes, the image does not update, even when the view is refreshed.

Is there a way to pull in the url every time selectImage is executed?

2      

This works perfectly for my needs.

Thanks.

1      

@andycairnsccl This is what i finally use to have this behavior: i use SDWebImage https://github.com/SDWebImage/SDWebImageSwiftUI It works really good but have the same refresh problem BUT the following trick works : use an image url with this format: http://mywebsite/images/myimage.jpg?time=1606039247 (use current unix timestamp). Then you just have to refresh the url (the timestamp) every time you want to refresh the image, and that´s it !

1      

Nigels code doesnt work for me. i only see the placeholder picture...

1      

@a3igner Were you using SwiftUI previews? As of Xcode 12.4 it seems like networking is disabled in the preview environment so the web image won't load in the preview window. When I run it on the simulator the remote image loads as expected though. It's also possible that the string URL passed in was invalid (I didn't like the fatal error when the URL couldn't be created - I opted to use an actual URL type in the initializers to make things cleaner)

1      

Save 50% in my Black Friday sale.

SAVE 50% To celebrate WWDC22, all our books and bundles are half price, so you can take your Swift knowledge further without spending big! Get the Swift Power Pack to build your iOS career faster, get the Swift Platform Pack to builds apps for macOS, watchOS, and beyond, or get the Swift Plus Pack to learn advanced design patterns, testing skills, and more.

Save 50% on all our books and bundles!

Sponsor Hacking with Swift and reach the world's largest Swift community!

Archived topic

This topic has been closed due to inactivity, so you can't reply. Please create a new topic if you need to.

All interactions here are governed by our code of conduct.

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.