Reputation: 9364
I'm trying to wait for Parse async functions in Swift to reload my UITableView
I'm not sure if Completion Handler is useful in this case. or Dispatch Async.
I'm really confused ! Can someone help out with this
var posts = [PFObject]()
for post in posts {
post.fetchInBackground()
}
tableView.reloadData() // I want to execute that when the async functions have finished execution
Upvotes: 1
Views: 715
Reputation: 6106
I have experimented a bit with the blocks and they seem to get called on the main thread, which means that any UI changes can be made there. The code I have used to test looks something like this:
func reloadPosts() {
PFObject.fetchAllIfNeededInBackground(posts) {
[unowned self] (result, error) in
if let err = error {
self.displayError(err)
}
self.tableView.reloadData()
}
}
if you are in doubt about whether or not the block is called on the main thread you can use the NSThread
class to check for this
print(NSThread.currentThread().isMainThread)
And if you want it to be bulletproof you can wrap your reloadData
inside dispatch_block_t
to ensure it is on the main thread
Edit: The documentation doesn't state anywhere if the block is executed on the main thread, but the source code is pretty clear that it does
+ (void)fetchAllIfNeededInBackground:(NSArray *)objects block:(PFArrayResultBlock)block {
[[self fetchAllIfNeededInBackground:objects] thenCallBackOnMainThreadAsync:block];
}
Upvotes: 0
Reputation: 1461
You want to use fetchAllInBackground:Block I've had issues launching a bunch of parse calls in a loop where it will take a lot longer to return all of them than expected. fetch documentation
It should look something like this:
PFObject.fetchAllInBackground(posts, block: { (complete, error) in
if (error == nil && complete) {
self.tableView.reloadData()
}
})
One thing to note is that in your example posts are empty and a generic PFObject. I'm assuming this is just for the example. Otherwise if you want to get all posts in Parse (as opposed to updating current ones) you will want to use PFQuery instead of fetching. query documentation
Upvotes: 2
Reputation: 42133
Here's a little class I made to help with coordination of asynchronous processes:
class CompletionBlock
{
var completionCode:()->()
init?(_ execute:()->() )
{ completionCode = execute }
func deferred() {}
deinit
{ completionCode() }
}
The trick is to create an instance of CompletionBlock with the code you want to execute after the last asynchronous block and make a reference to the object inside the closures.
let reloadTable = CompletionBlock({ self.tableView.reloadData() })
var posts = [PFObject]()
for post in posts
{
post.fetchInBackground(){ reloadTable.deferred() }
}
The object will remain "alive" until the last capture goes out of scope. Then the object itself will go out of scope and its deinit will be called executing your finalization code at that point.
Upvotes: 1
Reputation: 9042
You need to use fetchInBackgroundWithBlock
. Alternatively, if you want to wait until all have loaded and then update the UI, use PFObject's +fetchAllInBackground:block:
. Note that this is a class method, and would therefore be called as PFObject.fetchAllInBackground(...
. See documentation here.
Either way, because you're running in a background thread, you must update the UI on the main thread. This is normally done using dispatch_async
.
The other thing to watch out for is if you run fetchInBackgroundWithBlock
in a loop and collect all the results in an array, arrays are not thread safe. You will have to use something like dispatch_barrier
or your own synchronous queue to synchronise access to the array. Code for the second option is below:
// Declared once and shared by each call (set your own name)...
let queue = dispatch_queue_create("my.own.queue", nil)
// For each call...
dispatch_sync(queue) {
self.myArray.append(myElement)
}
Upvotes: 1
Reputation: 4044
Here is an example of using fetchInBackgroundWithBlock
which reloads a tableView
upon completion
var myArray = [String]()
func fetchData() {
let userQuery: PFQuery = PFUser.query()!
userQuery.findObjectsInBackgroundWithBlock({
(users, error) -> Void in
var userData = users!
if error == nil {
if userData.count >= 1 {
for i in 0...users!.count-1 {
self.myArray.append(userData[i].valueForKey("dataColumnInParse") as! String)
}
}
self.tableView.reloadData()
} else {
print(error)
}
})
}
My example is a query on the user class but you get the idea...
Upvotes: 0