0

I'm having an issue retrieving data from within an closure. I'm calling function called getWallImages which is supposed to return an array. I can print the contents of the array from within the closure, but outside of it the array is empty.

import Foundation
import Parse

class WallPostQuery {

    var result = [WallPost]()

    func getWallImages() -> [WallPost] { 
        let query = WallPost.query()!

        query.findObjectsInBackgroundWithBlock { objects, error in    
            if error == nil {     
                if let objects = objects as? [WallPost] {
                    self.result = objects
                    //This line will print the three PFObjects I have
                    println(self.result)
                }
            }
        }

        //this line prints [] ...empty array?
        println(result)
        return self.result
    }
}

Question

How do I get values out of a closure?

adolfosrs
  • 9,286
  • 5
  • 39
  • 67
Martin Muldoon
  • 3,388
  • 4
  • 24
  • 55

3 Answers3

5

That is because println(result) is executed BEFORE self.results = objects. The closure is executed asynchronously so it executes afterwards. Try making a function that uses results which can be called form the closure:

var result = [WallPost]()
    func getWallImages() {

        let query = WallPost.query()!

        query.findObjectsInBackgroundWithBlock { objects, error in

            if error == nil {

                if let objects = objects as? [WallPost] {
                    self.result = objects
                    //This line will print the three PFObjects I have
                    println(self.result)
                    self.useResults(self.result)
                }
            }
        } 
    }

    func useResults(wallPosts: [WallPost]) {
        println(wallPosts)
    }

}

Another solution to your problem, so that you can return it from that function is to create your own closure:

var result = [WallPost]()
    func getWallImages(completion: (wallPosts: [WallPost]?) -> ()) {

        let query = WallPost.query()!

        query.findObjectsInBackgroundWithBlock { objects, error in

            if error == nil {

                if let objects = objects as? [WallPost] {
                    self.result = objects
                    //This line will print the three PFObjects I have
                    println(self.result)
                    completion(wallPosts: self.result)
                } else {
                    completion(wallPosts: nil)
                }
            } else {
                completion(wallPosts: nil)
            }
        } 
    }

    func useResults(wallPosts: [WallPost]) {
        println(wallPosts)
    }

}
Swinny89
  • 7,273
  • 3
  • 32
  • 52
0

What is happening is that the method is returning before the closure executes.

Fundamentally, you're running into a problem with they way you are managing asynchronous callbacks.

Asynchronous vs synchronous execution, what does it really mean?

You need to create a way of notifying your caller from within your closure. You can achieve this by: requiring your own closure as an input parameters; using a delegate pattern; using a notification.

https://codereview.stackexchange.com/questions/87016/swift-ios-call-back-functions

Each has their benefits/drawbacks, and it depends on your particular situation. The simplest way to get started with async data fetches, is to pass in your own closure. From there, you can jump to another pattern such as the delegate pattern if the need arises.

Community
  • 1
  • 1
Drew Beaupre
  • 2,437
  • 1
  • 17
  • 17
0

I think the latter of println(result) is called before because findObjectsInBackgroundWithBlock is executed on background as its name suggests.

So, you can confirm result in the following way,

import Foundation
import Parse

class WallPostQuery {

    var result = [WallPost]() {
        didSet {
            println(result)
        }
    }

    func getWallImages() { 
        let query = WallPost.query()!

        query.findObjectsInBackgroundWithBlock { objects, error in    
            if error == nil {     
                if let objects = objects as? [WallPost] {
                    self.result = objects
                    //This line will print the three PFObjects I have
                    println(self.result)
                }
            }
        }
    }
}
pixyzehn
  • 762
  • 6
  • 15