UITableView datasource / reload race condition - ios

Issue:
I have a UITableView that is reloaded by a controller as soon as it receives an NSNotification. The data structure that the cellForRowAtIndexPath uses as the datasource may change while the table is refreshing.
Background:
Whenever the app's data model changes a NSNotification gets fired and my UITableViewController who handles the datasource of the UITableView gets notified to execute a "refreshReload" method. The "refreshReload" method retrieves the new data from the Model and then asks for [tableView reloadData]. This is classes MVC pattern where the model gets changed, the controller gets notified and the view gets updated. App crashes when there are quick Notifications back to back that change the data quickly. I feel that while the table is calling cellForRowAtIndexPath: the data structure that contains the data changes during the execution.
What would be a good pattern to follow to avoid this, is there a way to stop reloading of a table so that I can first perform the stop then change the datasource ?

If I understand your question correctly, I would use global flag to lock data changes.
e.g. use singleton pattern to hold your flag value. Check whether flag is locked or not. If flag is locked, do not call or disable data change methods. When UITableView is updating, lock flag until updating is finished.

The crash will be come if the data change very frequently i.e. at the time table is updating and table get another reload call.
To resolve crashes in your app:
First thing you can do is that avoid that much frequent calling of reload table. Maintain a Flag (in Notification observer method that you set for receiving notification) ,that flag tells that table need to reload now when user come to this tableview screen you create a timer that will call a method after (say) every one minute . In that method, check for flag , if it is YES then reload the table and Change Flag value to NO else there is nothing you need to do.
I hope that will resolve your problem for sure.

Related

Is it efficient to save text in Core Data in UITextView delegate method textViewDidChange?

I have multiple UITextView instances in my UIViewController and would like to save the typed-in text in Core Data when the view controller's view disappears or a specific button is clicked. Ideally, I would use the textViewDidEndEditing delegate method to save the changes to Core Data but the problem is that if a user changes a text view and then hits the said button, this delegate method is not called. So how should I save the changes?
One option I can think of is to leverage textViewDidChange - but that method is called each time a character is typed in. If I were to save the text to Core Data within this method, wouldn't that be inefficient?
Are there other ways you can recommend for me to achieve this?
It's unclear exactly what the relationship is between button pushing and saving data, but one way would be to leverage viewWillDisappear: and make your Core Data insertions fire once you know the text inputs are final and the user is leaving.
You could fire the same insertion method when the button is tapped.
You may have a local NSString variable to hold the updated value of your string and you can save it to CoreData when you finish your work with screen. It may be on :
- A button touch
- View disappearing
- An error ocured in page
If you do not want to have a save action, you can also set a timer to count the time starting from the last edit moment of textViewDidChange and you can trigger timer if there is no change for the last 3-5 seconds.

Context for UITableViewCell/UICollectionViewCell

This question probably looks similar to some other but I think it is not.
Going straightforward ...
Is it for you a best practice set an opaque context object (void * / id / AnyObject) to an UITableViewCell/UICollectionViewCell subclass in general a best practice?
Why this question? ... You probably know that UITableView / UICollectionView are builded in an asynchronous way, and this carry me to think that this can be a good solution to avoid crashes or wrong data usage.
Let me explain.
Probably the majority of iOS developer encountered at least one time the needs to subclass an UITableViewCell with a delegate object used to pass the cell to react to some interaction on the cell itself.
Doing so the delegate (ViewController) probably will search the index path of the cell to discover the related data object and do some process with the data. Ok but a lot of time it happens that the delegate message is delivered after the tableView was reloaded due to a remote data update but being an asynchronous process old cells are still on screen ...
In the worst case you will crash and in the best case you will act on the wrong data object.
So a solution to this could be, as asked before, an opaque object (context) carried by the cell subclass that will be available to the cell delegate getting back the context from the cell sent to the delegate message.
Obviously due to reuse the cell class must be cleaned up of the context during the prepareForReuse.
In this way TableViewCell do not know nothing about the model but it will act as postman for the delegate.
What do you think about?
but a lot of time it happens that the delegate message is delivered after the tableView was reloaded due to a remote data update but being an asynchronous process old cells are still on screen
You shouldn't get this issue if:
Your delegate message is sent synchronously on the main queue.
You deal with model and table view updates synchronously on the main queue.
If you have some delay between your model updating and your table UI reloading, you'll have lots of issues, not just the one you describe.
Essentially, I prefer the method you detailed of looking up the index path etc. over a context object added to the cell.

Why is my `configureCell:` method called every time I change the value of the object the cell corresponds to? (But I DON'T save it.)

I'm making an app where when you select a cell, you're segued to a new view for reading. The cell you tapped on corresponds to an object in the Core Data store (through NSFetchedResultsController) and that object is set as the value for the article property of the view that is being segued to.
Once there, I move the position of the user in the article as they read it. (So I alter the position attribute of article via article.position = ...)
However, this occurs very frequently as they read, often hundreds of times, and each time NSFetchedResultsController is detecting the change as an update, then calling configureCell:, which then runs through a bunch of configuration for that cell. As this configureCell: method is called so often (and I only want it to be called when they go back to the table view, as that's the only time the update is needed) it's causing a decent performance loss.
I don't get why it's calling it though. I'm not saving the data with NSManagedObjectContext into the Core Data store, so why does it care? I only call that when viewWillDisappear is called, indicating that they're leaving the view, likely to go back to the table view which is where I want it!
Basically, how do I get it to only call configureCell: when it needs to/when I ask it to write the data to Core Data? It's calling it hundreds of times as is.
It doesn't matter if you're not persisting the changes to Core Data objects to disk; a change to a Core Data managed object IS a change, and so NSFetchedResultsController acknowledges it.
You could avoid updating the tableview that's not visible by setting NSFetchedResultsController's delegate to nil in viewWillDisappear:, and setting it back to self in viewDidAppear:. Also, add a performFetch: after setting the delegate.
The "hidden" NSFetchedResultsController will still be receiving all the changes made to the article object in the view that's in the foreground, but will ignore them since it doesn't have a delegate.
When going back to the articles list view, it will have a delegate again, and it will be able to react to all changes.

Where should I load a UISwitch's state?

I am currently trying to load the UISwitch's state from NSUserDefaults in my view. These UISwitch's are in custom tableview cells that are loaded in the cellforrowatindexpath. If I put my code to load the switches states in the cellforrow method wouldn't that cause the view to lag a bit on startup since that method is getting called a few times and since I only need this code to be executed once?
Currently, I am loading it after the cellforrowatindexpath delegate method has been done doing its stuff which for me is in the viewdidload method. But the thing is, it doesn't look so nice because the switches are snapping into place while the user can see the switches which I do not want. If I put the code into the viewwillappear method, it causes the switches all to be off since they are not created yet.
So in what place should I load the switches state from NSUserDefaults so that it looks nice and is still efficient?
Thanks!
You can load the data for NSUserDefaults in the previous view (if it is there), and than just give this parameters through NSArray (f.e.), and set them after configuring the cell.

UITableView not showing new cell data

The first time my UITableView loads, my cellForRowAtIndexPath method gets correctly called. The subsequent times I show my IUTableView, this method is not called and the old data is shown.
Neither are the viewWillAppear method nor the numberOfRowsInSelection called.
Anyone know what could be wrong?
Thanks
Deshawn
When you get your new data, fire -reloadData on the table view. It'll run all its data source methods to get a fresh look at its source data.
Don't tie that reload on the display of the view. Tie it to when you receive the new data.

Resources