0

I have the following method which primarily uses the username parameter to load the user's picture from the backend Parse.

func getProfilePicture(username: String) -> UIImage
    {
        var tempImage:UIImage = UIImage(named: "sample-qr-code.png")!
        let query: PFQuery = PFQuery(className: "_User")
        query.whereKey("appUsername", equalTo: username)
        query.findObjectsInBackgroundWithBlock {
            (objects:[PFObject]?, error:NSError?) -> Void in
            for object in objects! {

                let imageFiles = object["ProfilePic"] as! PFFile

                imageFiles.getDataInBackgroundWithBlock({
                    (imageData: NSData?, error: NSError?) -> Void in
                    if (error == nil) {
                        tempImage = UIImage(data:imageData!)!
                    }

                })

            }

        }
        return tempImage
    }

In the first line of the method, I am assigning a value to tempImage for the simple reason that it can't be empty otherwise it throws nil exception. When the code above runs, it goes through all the stages and conditions (detected by logging using print function) but the returned image is still sample-qr-code.png instead of the one in the database. There are no errors thrown from backend and this line gets executed:

tempImage = UIImage(data:imageData!)!

Can someone help me solve why this is not showing picture from backend? I am guessing it could be something to do with initialization and scope, but not certain.

ksa_coder
  • 1,393
  • 3
  • 15
  • 38

2 Answers2

1

query.findObjectsInBackgroundWithBlock is running in the background. That means when your function returns, it will have the default image that you originally assigned to it.

And tempImage can be reset or assiged immediately, or up to a second after the getProfilePicture method returns the default image.

You need to redo how you are getting that image, e.g. get it synchronously (i.e. via findObjects:, potentially slow) or pass in an image view outlet and have the image written directly to the image view as soon as the background function completes successfully.

Michael Dautermann
  • 88,797
  • 17
  • 166
  • 215
1

This is happening because the function completes before the tasks within the closures do.

You should make tempImage a property of self and have the closures update that property.

class SomeClass {

    var tempImage: UIImage?

    func someFuncToGetStuff() {

        Parse.doParseStuffWithCompletion(fetchedPFFile) -> Void in

            let myImage = UIImage(data: fetchedPFFile)
            self.tempImage = myImage

            }

    }

}
Fred Faust
  • 6,696
  • 4
  • 32
  • 55
  • can you please elaborate what you mean here...property of itself? – ksa_coder Jan 01 '16 at 15:02
  • assuming that function exists inside of a class that's still referenced in memory if you make your UIImage a property of that class then the task within the closure will have access to it even after the function that initiated the closure has been completed. – Fred Faust Jan 01 '16 at 15:04
  • added an example in the answer, obviously you want to do stuff conditionally (like if things aren't nil, etc.) – Fred Faust Jan 01 '16 at 15:08
  • The only thing is: I have my view controller and a separate swift class called utilities where the methods for communicating with Parse reside. This method resides in utilities and I need to return an image so that I can use it in view controller. – ksa_coder Jan 01 '16 at 15:09
  • You may want to add your own completion handler to a function that calls the parse stuff and get your result within it on your view controller. There you can directly update the UIImageView that needs the image. – Fred Faust Jan 01 '16 at 15:11
  • The answer on the duplicate question has exactly what you need. – Fred Faust Jan 01 '16 at 15:20