Home > Blockchain >  Async/Await: "out of range error" when trying to access array filled with downloaded image
Async/Await: "out of range error" when trying to access array filled with downloaded image

Time:09-26

I tried to download successfully uploaded images to storage with the code:

  func retrieveAllPictures(helloid: String) async {
        //Referenzen zu den Datenbanken
        let db = Firestore.firestore()
        //Alle Foto Ids in einem Array speichern
        let result = try? await db.collection("events").document(helloid).getDocument()
        allPictureIDs = (result?.data()!["pictures"] as? [String])!
        var image = UIImage()
        
        
        for path in allPictureIDs {
            let storage = Storage.storage().reference()
            let fileRef = storage.child(path)
            
  
      try? await fileRef.getData(maxSize: 1 * 1024 * 1024) { data, error in
                if let error = error {
                    return
                } else {
                    image = UIImage(data: data!)!
                    self.retrievedEventImages.append(image)
                }
            }
    }
}

this is how I try to access the array at index 0:

struct finalEventView: View {
    @EnvironmentObject var viewModel: AppViewModel
    var id: String
    var body: some View {
        
        NavigationView {
            VStack{
               
                if (viewModel.retrievedEventImages[0] != nil){
                Image(uiImage: viewModel.retrievedEventImages[0]!)
                }
                /*
                ForEach(viewModel.EventImages, id: \.self){ image in
                    Image(uiImage: image)
                        .resizable()
                        .frame(width: 110, height: 110)
                }*/
                Button(action: {
                    Task {
                        try? await viewModel.retrieveAllPictures(helloid: self.id)
                    }
                }, label: {Text("print image arr")})
            }}
        .onAppear{
            Task {
                try? await viewModel.retrieveAllPictures(helloid: self.id)
            }
        }
    }
}

when trying to debug the code, I see that retrievedEventImages is filled with the UIImages still when trying to access the array at index 0 I get an out of range error maybe someone knows how to fix it, any help is appreciated

CodePudding user response:

Never access an item of an array in a SwiftUI view rendering area by index.

In most cases the view is rendered the first time while the array is empty which causes the out of range crash.

This kind of checking for the existence of an item is unswifty anyway, just use first and Optional Binding

if let firstImage = viewModel.retrievedEventImages.first {
   Image(uiImage: firstImage)
}

Edit: Apparently there is no async version of getData(). You can adopt async/await with a Continuation

do {
    let data : Data = try await withCheckedThrowingContinuation { continuation in
        fileRef.getData(maxSize: 1 * 1024 * 1024) { data, error in
            if let error = error {
                continuation.resume(throwing: error)
            } else {
                continuation.resume(returning: data!)
            }
        }
    }
    let image = UIImage(data: data)!
    self.retrievedEventImages.append(image)
} catch {
    print(error)
    // or better show a message to the user
}

CodePudding user response:

See @vadian answer for an explanation of part of the issue.

To add an await/async code solution as well. I'm doing this on macOS - the iOS solution is similar.

Task {
    let resultData = try! await imageRef.data(maxSize: 1 * 1024 * 1024)
    let image = NSImage(data: resultData) //UIImage for iOS
    self.retrievedEventImages.append(image)
}

Firebase used the await/async func data instead of getData to avoid naming collisions.

There's probably more up-to-date info but see git 8289 for further reading.

  • Related