1

In such a function I get a nil value but I do not understand why. The code in the middle returns an image (and I'm sure about it, I've also checked with some print statements). I do not know how is it possible that it always returns nil. It is like it ignores all the code running in the middle and considers just the first and last statement.

func getImagesDownloaded(reference: StorageReference) -> UIImage {
    var imagePassedIn : UIImage?
    reference.getData(maxSize: 10*1024*1024) { (data, error) in

        guard let imageObject = UIImage(data: data!) else {print("Error has occurred: \(String(describing: error?.localizedDescription))"); return}
        imagePassedIn = imageObject
    }
    if imagePassedIn == nil {
        print("Error, getImagesDownloaded is not working")
    }
    return imagePassedIn!
}
Dávid Pásztor
  • 51,403
  • 9
  • 85
  • 116
Niccolò Diana
  • 119
  • 2
  • 13
  • You need completion handler here. You can check [this](https://stackoverflow.com/questions/30401439/how-could-i-create-a-function-with-a-completion-handler-in-swift). – Kamran Apr 03 '19 at 10:53

3 Answers3

3

The issue is that StorageReference.getData is an asynchronous function, but you're trying to synchronously return a value. You need to use a completion handler to return the asyncronously retrieved value.

func getImagesDownloaded(reference: StorageReference, completion: (UIImage?,Error?)->()) {
    reference.getData(maxSize: 10*1024*1024) { (data, error) in
        guard error == nil, let data = data else {
            completion(nil,error)
            return
        }
        guard let image = UIImage(data: data) else {
            completion(nil, FirebaseErrors.expectedImage)
            return
        }
        completion(image,nil)
    }
}

enum FirebaseErrors: Error {
    case expectedImage
}

Then you need to use it like this:

getImagesDownloaded(reference: yourStorageReference, completion: { image, error in
    guard let image = image, error == nil else {
        print(error)
        return
    }
    // Assign your image to a UIImageView or do anything else with it inside the closure (before the ending `}`)
    yourImageView.image = image
})
Dávid Pásztor
  • 51,403
  • 9
  • 85
  • 116
  • You just gave your 1000th answer, congrats :D – emrepun Apr 03 '19 at 11:07
  • How can I return a value from it? Would the following statement work? let imageDownloaded = self.getImagesDownloaded(reference: storagePath, completion: { (image, error) in if error == nil { return image } else {print("ERROR: \(error?.localizedDescription)")} }) – Niccolò Diana Apr 03 '19 at 11:13
  • upvoted! Would you like to give the **usage** example as well for easy adaptation? – Kamran Apr 03 '19 at 11:50
0

You are using a closure and the imageObject is probably not returned when you are doing the nil check. Swift is executed line by line and when you have async code it executes the next line and doesn't wait for the result.

you should move the imagePassedIn check in the closure.

Michael
  • 1,030
  • 14
  • 29
0

You're setting imagePassedIn inside a completion block. This means that when you are ready to set it, you've already retuned it.

In a few words, when using completion blocks, the code bellow won't be waiting for it to finish in order to execute.

Update your functions to this:

func getImagesDownloaded(reference: StorageReference, _ completion: (UIImage) -> ()) {
    reference.getData(maxSize: 10*1024*1024) { (data, error) in

        guard let data = data, let imageObject = UIImage(data: data) else {print("Error has occurred: \(String(describing: error?.localizedDescription))"); return}
        completion(imageObject)
    } else {
        print("Error, getImagesDownloaded is not working")
    }
}
Pedro Carrasco
  • 148
  • 1
  • 8
  • Is it going to return a UIImage in this way? let imageDownloaded = self.getImagesDownloaded(reference: storagePath, { (image) in return image }) – Niccolò Diana Apr 03 '19 at 11:08
  • It's not going to return since it's asynchronous. Instead call & use the "returned" image like: self.getImagesDownloaded(reference: storagePath) { image in // do what you want with `image` } – Pedro Carrasco Apr 03 '19 at 11:59