NEW: Start my new Ultimate Portfolio App course with a free Hacking with Swift+ trial! >>

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?

   

What is an example a the URL?

   

Hacking with Swift is sponsored by Instabug

SPONSORED Catch bugs as soon as they happen and know exactly why a crash occurred. Instabug's SDK grabs all the logs they need to fix bugs, crashes and performance issues in minutes instead of days. Get screenshots, device details, network logs, repro steps, and tons of other critical insights needed to resolve issues and prioritize product backlogs straight from your dashboard. It only takes a minute to integrate!

Get started now

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

2      

thanks Nigel i will give that a try.

   

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

   

The @StateObject is for Xcode 12

   

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

   

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 ?

1      

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?

1      

This works perfectly for my needs.

Thanks.

   

@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 !

   

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

   

@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)

   

Hacking with Swift is sponsored by Instabug

SPONSORED Catch bugs as soon as they happen and know exactly why a crash occurred. Instabug's SDK grabs all the logs they need to fix bugs, crashes and performance issues in minutes instead of days. Get screenshots, device details, network logs, repro steps, and tons of other critical insights needed to resolve issues and prioritize product backlogs straight from your dashboard. It only takes a minute to integrate!

Get started now

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

Reply to this topic…

You need to create an account or log in to reply.

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.