Reputation: 588
I have an application that pulls information from a Parse database, and displays it in a UITableView. I pull the information from parse in the viewWillAppear function, and i display it in the tableView(cellForRowAtIndexPath) function. Sometimes i receive an error because the array that stores the Parse information has a length of 0, and i try to access information at an index outside of the bounds of the array. I believe this is because the cellForRowAtIndexPath is getting called before the viewWillAppear is finished running. Is this possible or is my error definitely coming from somewhere else?
EDIT: The error does not occur every time, and i cannot find a way to reproduce it
override func viewWillAppear(animated: Bool) {
//begin ignoring events until the information is finished being pulled
UIApplication.sharedApplication().beginIgnoringInteractionEvents()
resultsArray.removeAll(keepCapacity: false)
//run query
let query = PFQuery(className: "Answers")
query.findObjectsInBackgroundWithBlock { (objects, error) -> Void in
if let objects = objects {
//append information to the resultsArray
}
}
self.tableView.reloadData()
}
}
//information is now pulled, so allow interaction
UIApplication.sharedApplication().endIgnoringInteractionEvents()
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! answerCell
// THIS IS WHERE THE ERROR OCCURS
resultsArray[indexPath.row].imageFile.getDataInBackgroundWithBlock { (data, error) -> Void in
//set image within cell
}
return cell
}
Upvotes: 3
Views: 2039
Reputation: 114875
I would suggest that you load your data from Parse into a temporary array and then assign this to your property array right before you call reloadData
- this will avoid any potential race conditions and remove the need for the removeAll
which is potentially a big part of your problem;
override func viewWillAppear(animated: Bool) {
//begin ignoring events until the information is finished being pulled
UIApplication.sharedApplication().beginIgnoringInteractionEvents()
//run query
let query = PFQuery(className: "Answers")
query.findObjectsInBackgroundWithBlock { (objects, error) -> Void in
var localArray=[SomeType]()
if let objects = objects {
//append information to the localArray
}
}
self.resultsArray=localArray
self.tableView.reloadData()
}
}
//information is now pulled, so allow interaction
UIApplication.sharedApplication().endIgnoringInteractionEvents()
}
Upvotes: 1
Reputation: 846
Actually if your callback not access to self.tableView
, everything will go on as you think as usual. You can have a try.
It happened to me when I access to the view on the screen in init
method viewDidLoad
method called before init
ends.
Anyway, you should know that fact. And you access to your tableView
in callback (called before viewWillAppear
finishing) which needs cellForRowAtIndexPath
.
Upvotes: 0
Reputation: 4803
Looks like in viewWillAppear
you have a background block findObjectsInBackgroundWithBlock
that has some work to do in a different thread (AKA off the main thread), that means that viewWillAppear will finish while the block will get a callback.
This explains why cellForRowAtIndexPath
is being called after viewWillAppear
finishes, because of the callback block.
That means that everything is alright and viewWillAppear
actually do finish a legit "run".
You can actually put a breaking point inside the callback method (in viewWillAppear
) and a breaking point inside cellForRowAtIndexPath
and see when the callback happens while cellForRowAtIndexPath
is being called.
If you need a different method from Parse perhaps you should look in their documentation.
Upvotes: 0