Enable, disable NSFetchedResultsController in a background enabled application - ios

I am working on a VOIP/Chat application that receives data even while in the background.
I need to disable the NSFetchedResultsController when the app is moving to the background to prevent UI changes in the background.
I do it like this -
- (void)applicationWillResignActive
{
[super applicationWillResignActive];
self.fetchedResultsController.delegate = nil;
}
- (void)applicationDidBecomeActive
{
[super applicationDidBecomeActive];
self.fetchedResultsController.delegate = self.fetchResultControllerDelegate;
}
I have noticed that I don't need to call [self.tableView reloadData] when coming back to foreground. (EDIT : just to clarify the Core Data DB was updated with new data while the app was in the background and the fetchedResultsController.delegate was nil).
And the table updates itself right after reassigning the fetchedResultsController.delegate.
What makes it update, does the fetchedResultsController preforms fetch when reassigned ?
Are there any pitfalls to this approach that can make a conflict between the tableView and the fetchedResultsController ?
Thanks

Fetched results controllers provide the following features:
Optionally monitor changes to objects in the associated managed object context, and report changes in the results set to its delegate (see “The Controller’s Delegate”).
Optionally cache the results of its computation so that if the same data is subsequently re-displayed, the work does not have to be repeated (see “The Cache”).
A controller thus effectively has three modes of operation, determined by whether it has a delegate and whether the cache file name is set.
No tracking: the delegate is set to nil.
The controller simply provides access to the data as it was when the fetch was executed.
Memory-only tracking: the delegate is non-nil and the file cache name is set to nil.
The controller monitors objects in its result set and updates section and ordering information in response to relevant changes.
Full persistent tracking: the delegate and the file cache name are non-nil.
The controller monitors objects in its result set and updates section and ordering information in response to relevant changes. The controller maintains a persistent cache of the results of its computation.

What makes it update, does the fetchedResultsController preforms fetch when reassigned ?
It only does a fetch the first time you create a fetched results controller. After that, it is notified of any change to any object in the database as the changes happen, and evaluates whether to add or remove the object to the list of results. It never does a second fetch (except maybe if you get a low memory warning?).
Fetches are slow, it has to read flash memory and that takes milliseconds. However when an object is being changed, it's in RAM so operations only take nanoseconds. This is why it tries hard not to ever look at the sqlite database.
Are there any pitfalls to this approach that can make a conflict between the tableView and the fetchedResultsController ?
I'm not sure, maybe. If you haven't found any problems in testing, then I think you should assume it's fine. But be sure to test it again carefully when iOS 8 betas are available mid next year, so you can fix any problems before it's released to the public.
If you're worried about RAM usage, you should destroy the fetched results controller. But beware it will take a fairly significant amount of time to create it again, when the user switches back to your app.

Related

Core Data issue

I implemented core data in my app.
I am fetching data in ViewWillAppear method.
I am assign fetch result to local array.
Now, I make change on TextFiledDidEndEditing method to local array, but not saved to persistence store.
But when I again come to that view & try to re-fretch on ViewWillAppear method then that changed remain as it is.
Help to solve this
Thank you
NSFetchRequest has a property includesPendingChanges, which has a default of true. Just bear this in mind.
NSManagedObjectContext can tell you about insertedObjects, updatedObjects and deletedObjects.
The above all relates to dirty context state - changes you've made but which haven't been saved yet.
So while you aren't saving your change the context is still dirty with the change and each time you make a request the managed objects you get back will have those changes. The only thing you can filter out is inserted or deleted objects by using the fetch request flag (though that isn't your case here).
If you want to get rid of the changes you should use an undo manager, so rollback or reset the context.

How to avoid calling a method multiple times when the method takes long time to complete

There are several view controllers in my app where I need to sync the local contents with server using a method running in a background thread. Sometimes I need to insert data to my database on server if user has created any. The approach I am using here is to set a flag(something like isSynced = NO) on objects that I need to sync with server (there objects are in Core Data). When the syncing is complete my method will get rid of the flag(e.g. isSynced = YES) so it won't be sent again next time.
Now the problems is that the syncing method takes very long to complete(1 or 2seconds.). If now user pops out this particular view controller and swiftly comes back the previous call is still in progress and next one will be kicked off. The consequence is that there might be duplication in database.
My approach now is the make the syncing method to be called by a Singleton object:
#property (nonatomic) BOOL isSyncing;
//every time before syncing. check if object is available for syncing
if (!isSyncing) {
isSyncing = YES;
// sync server
// when complete
isSyncing = NO;
// post notification to view controller to reload table
} else {
// cancel because previous call is not finished
}
My concern is that if the call is cancelled my view controller will not be able to receive the notification is waiting for. I can fix this by posting another notification in the event of cancelation. I am wondering if this is the right to do this because I think that this problem should be pretty common in iOS development and there should be a standard way to deal with it
Your singleton approach may not be necessary. I don't see the harm in sending a database insert for each new object. You will still need to ensure each object is synched. That is, update the "isSynched" flag. Keep each object that needs to be synced in a "need to synch" list.
Then, update the "isSynced" flag by performing a background query on the database to check if the object exits. Then, use the result of the query to set the isSynched flag.
If the query result indicates the object is not in the database you then resend the object and leave it's "isSynced" flag set to NO.
If the query result indicates the object is in the database, set the "isSynced" flag to YES and remove it from your "need to synch" list.
An approach for preventing duplicate database entries is to make a unique key. For example, tag each with a hash based on the time and date. Then configure the table to ensure each key is unique.

NSFetchedResultsControllerDelegate vs NSFetchedResultsController

After reading lots of documentation and examples of Core Data framework, I still don't quite get it. What happens is that I sort of understand parts of a sample code or documentation, but often can't fit it into a larger picture. The second time I read the same code, I still need lots of time to figure it out just like the first time. It's so frustrating.
The NSFetchedResultsControllerDelegate vs NSFetchedResultsController is one of those concepts in Core Data that confuse me.
I think what I need is a simple and conceptual explanation, maybe analogy will be helpful.
All your core data objects have to be accessed via managed object context.
Think of NSFetchRequest as a representation of the database query. When you tell the NSManagedObjectContext (MOC) to fetch data, you give it a fetch request, so it knows what to fetch.
Now, let's say you have a table view. You make a fetch, and have all the data, even for the stuff you don't need to display. Each time the table view changes, and you need to refetch the data. There are a few other issues with just using a fetch request, though it can be done.
To solve some of these problems, enter NSFetchedResultsController (FRC). It manages the database so that you only have the objects in memory that are actually needed at the time. Furthermore, it hooks into the MOC so that when the database changes, it automatically changes its own data.
So, you create a FRC and give it a fetch request. Now, it manages the data so it only has in memory what you want. But, you need to tell it what to grab, and it need to tell you when it has data.
Thus is where the NSFetchedResultsControllerDelegate comes in.
The delegate is the glue between the table view (or some other component) and the FRC. The delegate methods are the communication channel that informs the FRC what data to get, when to get it, then hand it off to the table view.
EDIT
Yes, FRC manages the actual data part. However, when its data changes, it has to have some way to notify the guy who is watching the data. That's what the delegate is for. Let's take a different attack by looking at the delegate methods.
Imagine that the database has been changed in some way. The FRC, through its special magical incantations, has noticed the change, and, like a teenager with a new iPhone, needs to tell someone.
Specifically, again, in your case, it needs to tell the table view that is responsible for displaying the data to the user. Well, how is it going to tell the table view that the data has changed? There are actually several patterns used in iOS, and in this case, we use a delegate.
The guy who is interested in receiving this information from the FRC give him a pointer to an object that implements the delegate methods. When the FRC wants to notify the guy interested, it calls the appropriate methods on the object that is was given as the delegate.
Consider a change has happened. The FRC code would look something like this (ultra-simplified but to give the algorithmic idea).
[delegate controllerWillChangeContent:self];
// Process all the changes...
for (SectionChangeInfo *info in changedSections) {
[delegate controller:self didChangeSection:info.sectionInfo atIndex:info.index forChangeType:info.changeType];
}
for (ObjectChangeInfo *info in changedObjects) {
[delegate controller:self didChangeObject:info.object atIndexPath:info.indexPath forChangeType:info.changeType newIndexPath:index.newIndexPath];
}
[delegate controllerDidChangeContent:self];
Thus, the FRC can tell "somebody" about changes when they occur. In your case, when you give the FRC a delegate, it will call those methods, thus giving you a chance to handle the changes when they happen.
The other delegate method is called to ask the delegate what it wants to use as a section title. So, assume the FRC needs to know what to use, it will call...
NSString *sectionTitle = [[section substringToIndex:1] uppercase];
if ([delegate respondsToSelector:#selector(controller:sectionIndexTitleForSectionName:)]) {
sectionTitle = [delegate controller:self sectionIndexTitleForSectionName:section];
}

Not getting current data in UITableView after XML Parse (using NSFetchedResultsController)

Struggling a bit here...
My view controller adheres to following protocols
In my init method I will check a remote server to get an updated XML file... parse the XML file, and write the contents to Core Data.
My tableview's content is managed with NSFetchedResultsController that displays this Core Data.
My Problem:
NSFetchedResultsController seems to be getting the data before the Core Data update from the remote file takes place. I've verified the database is being updated properly and if I run a second time the TableView will show the correct data.
Maybe I'm just not doing the reloadData in the proper place? I have implemented
-(void)controllerDidChangeContent:(NSFetchedResultsController *)controller {
[myTableView reloadData];
}
Also, after the parser completes and the new data has been written to core data I'm trying this:
-(void)parserDidEndDocument:(NSXMLParser *)parser {
[myTableView reloadData];
}
Anyone have any ideas? Let me know what extra code might be useful to post. Thanks!
you may want to check the following (from Apple's docs)
A controller thus effectively has three modes of operation, determined by whether it has a delegate and whether the cache file name is set.
No tracking: the delegate is set to nil.
The controller simply provides access to the data as it was when the fetch was executed.
Memory-only tracking: the delegate is non-nil and the file cache name is set to nil.
The controller monitors objects in its result set and updates section and ordering information in response to relevant changes.
Full persistent tracking: the delegate and the file cache name are non-nil.
The controller monitors objects in its result set and updates section and ordering information in response to relevant changes. The controller maintains a persistent cache of the results of its computation.
it sounds like you want full persistent tracking. So you probably want to make sure you have the delegate set (which you probably already have done) and set the cache to non nil
You may also want to make sure you are saving your managedObjectContext after you are done parsing. After saving, make sure to perform the fetch again.
NSError *error;
BOOL success = [controller performFetch:&error];
if (!success)
NSLog(#"Core Data Fetch Error: %#"error);
It could be that the app is saving the context when it is exiting and that is why you are seeing the data when you relaunch.
Good luck

Serious Application Error. This is usually a bug within an observer of NSManagedObjectContextObjectsDidChangeNotification. CoreData could not fulfill [duplicate]

My program does work like link below:
Update results of NSFetchedResultsController without a new fetch
show result of NSFetchedResultsController to UITableView
get new object from web service and store it to core data (in same view controller, with RestKit)
update table view with notification of NSFetchedResultsController delegate
The implementation of NSFetchedResultsControllerDelegate is copied from Apple's Core Data project and my predicated is:
[NSPredicate predicateWithFormat:#"isMyTest == TRUE"]
If the property update goes from TRUE to FALSE, it removes rows from the tableview (because the object for the row is in fetchedObjects of the NSFetchedResultsController)
However, if the property update goes from FALSE to TRUE, the NSFetchedResultsController notifies nothing, so the new data cannot be seen in table view. If I update BOTH NSFetchedResulsController and UITableView manually, it shows the new data.
I thought NSFetchedResultController watches all changes in persistent store, is it too big hope? :D
(I really want to do that because other view controller can update persistent store, then it is hard to update the view controller.)
If so, can you let me know how can I update NSFetchedResultsController in beautifully way?
(update)
in reference of NSfetchedResultsController, I read words below:
A controller thus effectively has three modes of operation, determined
by whether it has a delegate and whether the cache file name is set.
No tracking: the delegate is set to nil. The controller simply
provides access to the data as it was when the fetch was executed.
Memory-only tracking: the delegate is non-nil and the file cache name
is set to nil. The controller monitors objects in its result set and
updates section and ordering information in response to relevant
changes.
Full persistent tracking: the delegate and the file cache name are
non-nil. The controller monitors objects in its result set and updates
section and ordering information in response to relevant changes. The
controller maintains a persistent cache of the results of its
computation.
"Full persistent tracking" does not mean what I want? I set up cacheName, but it works same.
I thought NSFetchedResultController watches all changes in persistent
store, is it too big hope?
The FRC only automatically observes the objects returned by its fetch. This is normally not a problem as changes to objects monitored by an FRC should arise from either the tableview UI or a background context. In the former, the FRC is alerted to the change and in the latter, merging the foreground and background context will trigger the FRC to update.
It sounds like you've got code changing values but you're not notifying the FRC that you've made any changes. (If you got a tableview displaying all objects whose isMyTest == TRUE then obviously you can't access objects from the UI whose isMyTest == FALSE.) In that case, you need to register the tableview controller for:
NSManagedObjectContextObjectsDidChangeNotification
… notifications from the context so that you can tell the FRC to update for changes made outside its observation.
I'm sorry to my mistake, I tested with new test project.
CoreData DOES full-tracking of whole persistent store.
This means, if new object that is suitable to predicate of NSFetchedResultsController, delegate will notify it.

Resources