sfung3
sfung3

Reputation: 2377

How to lazy load image from data type

Is it possible to lazy load images that I have stored on a local database stored as Data?

Image(uiImage: UIImage(data: realm.imageData) ?? UIImage(named: "NotFound")!)

The computing of UIImage(data: realm.imageData) makes switching to and from this tab slow.

This is user inputted images into the database so the range of images could be from 0 to a lot.

(hopefully) minimum reproducible code

struct ContentView: View {
    
    var dataArr: [Data] = []
    
    var body: some View {
        
        ForEach(dataArr, id: \.self) { imageData in
            Image(uiImage: UIImage(data: imageData) ?? UIImage(named: "NotFound")!)
        }
    }
}

The problem is that all the processing is happening on tab switch instead of on appear.

note: these images are generated on device and and there is no reference to the URL. it has to be stored as data.

Upvotes: 1

Views: 431

Answers (1)

Jay
Jay

Reputation: 35648

In general, Realm is not a good datastore for images.

There's a hard limit to the size of a single property (16Mb) and images can easily go well beyond that. Please see my answer here for more in depth info.

If the images are small, thumbnails for example, that would work and you should not really see any performance issues.

The main issue I see in the question is using the Array to store realm objects.

var dataArr: [Data] = [] //Dont do this

With Array's, Realm loses its lazy-loading nature and all of the objects are crammed into memory - that can overwhelm the device quickly if the images are large. It can also affect app performance and UI responsiveness.

The other downside is that it disconnects the grouping of objects from their underlying data. For example, suppose the app displays images of users and a new user signs up. With the array, the app will not be aware of that change.

Please use @ObservedResults to properly work with Realm objects instead.

There are many benefits; the objects continue to be lazily loaded, it implicitly opens the Realm and retrieves the objects, it's a live reflection of those objects and it makes writing simpler as well. Also, if the data is filtered as users are added, changed or removed, the Results stay aligned and the UI can be updated.

If the object looked like this

class ImageObject: Object {
  @Persisted var imageData: Data
  ...

The code would be something like this

@ObservedResults(ImageObject.self) var imageObjectResults

    var body: some View {
        NavigationView {
            VStack {
                List {
                    ForEach(imageObjectResults) { anImageObject in
                        let theData = anImageObject.imageData
                        //do something with theData

Upvotes: 2

Related Questions