Entities saved to RKManagedObjectStore's mainQueueManagedObjectContext disappear on next build - ios

Using RestKit v0.20.0-rc1, I've managed to successfully create CoreData mappings and import objects from bundled JSON files and have the data persist for multiple builds. However, when I create my own entity and save it, the entity disappears immediately upon next build if I use [RKManagedObjectStore defaultStore].mainQueueManagedObjectContext, but persists properly if I use [RKManagedObjectStore defaultStore].persistentStoreManagedObjectContext.
UserAccount *userAccount = [NSEntityDescription insertNewObjectForEntityForName:entityName inManagedObjectContext:managedObjectContext];
userAccount.userID = #(userID);
[userAccount addContactMethodsObject:phone];
NSError *error = nil;
if(![managedObjectContext save:&error])
NSLog(#"%#", error);
Using either managedObjectContext saves without errors in the above code, and any fetches from the same context returns the entity properly. But upon subsequent builds, fetches will always return nil if I use mainQueueManagedObjectContext, even though the above code is run on the main thread.
Is there anything I'm missing?

When you use save: on a context created using mainQueueManagedObjectContext, it won't persist its changes to the store. According to the documentation for RKManagedObjectStore (bolding mine):
The managed object context hierarchy is designed to isolate the main
thread from disk I/O and avoid deadlocks. Because the primary context
manages its own private queue, saving the main queue context will not
result in the objects being saved to the persistent store. The primary
context must be saved as well for objects to be persisted to disk.
If you want to persist your changes and still use a mainQueueManagedObject context, try using
- (BOOL)saveToPersistentStore:(NSError **)error
That will kick the changes up the context hierarchy.
The relevant documentation for RKManagedObjectStore can be found at http://restkit.org/api/latest/Classes/RKManagedObjectStore.html#//api/name/persistentStoreManagedObjectContext
The documentation for restkit's NSManagedObject category can be found at
http://restkit.org/api/0.20.0-pre3/Categories/NSManagedObjectContext+RKAdditions.html

Related

Clearing a context in Core Data: reset vs deleting registered objects?

I was looking for posts regarding to this, but I don't fully understand... What is the difference between:
[context reset];
and:
for (NSManagedObjectID *objId in objectIds) {
[context deleteObject:[context objectWithID:objId]];
}
Or are they equivalent?
Thanks
Using reset puts the managed object context back to the state it was in when you first created it-- before you had performed any fetches, created any new objects, etc. If you have any managed objects in memory that were fetched from this context, they're now unusable. Using reset does not affect the persistent store file. All instances still exist afterward, they're just not in memory. They can be fetched again.
Using deleteObject removes the object from the persistent store. It does not exist any more. It can't be fetched anymore because it doesn't exist.

NSIncrementalStore called with NSSaveChangesRequest.deletedObjects == nil

I am in the process of writing an NSIncrementalStore that is backed by a REST service. It works nicely for POSTing and GETing objects, but when DELETEing them I encounter the following problem:
test calls [context delete: object];
context.deletedObjects contains object (as one would expect)
test calls [context save: &error];
iOS calls NSIncrementalStores executeRequest:withContext:error: with a NSSaveChangesRequest; when the call arrives both context.deletedObjects == nil and saveChangesRequest.deletedObjects == nil, so my code cannot find out which objects to DELETE from the server.
How can it happen that deletedObjects is set to nil between the call to the context's save and its invocation its NSIncrementalStore?
UPDATE possible cause: object.objectID is still a temporaryID following save. The NSIncrementalStore is currently obtaining object IDs from newObjectIDForEntity:referenceObject: and the description from Apple's documentation somehow does not seem to apply yet:
New objects inserted into a managed object context are assigned a
temporary ID which is replaced with a permanent one once the object
gets saved to a persistent store.
The trouble was that I did not yet store enough information on the server to recreate (permanent) object IDs during fetches.
This solves the problem in NSIncrementalStore:
When executeRequest:withContext:error: receives an NSSaveChangesRequest it extracts (in my case) reference strings from saved objects with referenceObjectForObjectID: and stores these (along with objects' other attributes) on the server.
When executeRequest:withContext:error: receives an NSFetchRequest it obtains reference strings (along with objects' other attributes) from the server and recreates objects with [context objectWithID: [self newObjectIDForEntity: entity referenceObject: referenceString]].

NSFetchedResultsController feeding table view while background update of same persistent store causes deadlock

Still working on converting an app over from downloading information every time it uses or displays it, to caching it on-phone using CoreData (courtesy of MagicalRecord). This is on iOS 7
Because we don't have a data-push system set up to automatically update the phone's cached data whenever some data changes on the backend, I've been thinking over the last many months (as we worked on other aspects of the app) how to manage keeping a local copy of the data on the phone and being able to have the most up to date data in the cache.
I realized that as long as I still fetch the data every time :-( I can use the phone's CoreData backed cache of data to display and use, and just use the fetch of the data to update the on-phone database.
So I have been converting over the main data objects from being downloaded data making up a complete object, to these main data objects being light stand-in objects for CoreData objects.
Basically, each of the normal data objects in the app, instead of containing all the properties of the object internally, contains only the objectIDof the underlying CoreData object and maybe the app specific ID internally, and all other properties are dynamic and gotten from the CoreData object and passed through (most properties are read-only and updates are done through bulk-rewriting of the core data from passed in JSON)
Like this:
- (NSString *)amount
{
__block NSString *result = nil;
NSManagedObjectContext *localContext = [NSManagedObjectContext MR_newContext];
[localContext performBlockAndWait:^{
FinTransaction *transaction = (FinTransaction *)[localContext existingObjectWithID:[self objectID] error:nil];
if (nil != transaction)
{
result = [transaction.amount stringValue];
}
}];
return result;
}
Occasionally there is one that needs to be set and those look like this:
- (void)setStatus:(MyTransactionStatus)status
{
[MagicalRecord saveWithBlock:^(NSManagedObjectContext *localContext) {
FinTransaction *transaction = (FinTransaction *)[localContext existingObjectWithID:[self objectID] error:nil];
if (nil != transaction)
{
transaction.statusValue = status;
}
} completion:^(BOOL success, NSError *error){}];
}
Now, my issue is that I have a view controller that basically uses an NSFetchedResultsController to display stored data from the local phone's CoreData database in a table view. At the same time as this is happening, and the user may start to scroll through the data, the phone spins off a thread to download updates to the data and then starts updating the CoreData data store with the updated data, at which point it then runs an asynchronous GCD call back on the main thread to have the fetched results controller refetch its data and and tells the table view to reload.
The problem is that if a user is scrolling through the initial fetched results controller fetched data and table view load, and the background thread is updating the same Core Data objects in the background, deadlocks occur. It is not the exact same entities being fetched and rewritten (when a deadlock occurs), i.e., not that object ID 1 is being read and written, but that the same persistent data store is being used.
Every access, read or write, happens in a MR_saveWithBlock or MR_saveWithBlockAndWait (writes/updates of data) as the case may be, and a [localContext performBlock:] or [localContext performBlockAndWait:] as may be appropriate. Each separate read or write has its own NSManagedObjectContext. I have not seen any where there are stray pending changes hanging around, and the actual places it blocks and deadlocks is not always the same, but always has to do with the main thread reading from the same persistent store as the background thread is using to update the data.
The fetched results controller is being created like this:
_frController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
managedObjectContext:[NSManagedObjectContext MR_rootSavingContext]
sectionNameKeyPath:sectionKeyPath
cacheName:nil];
and then an performFetch is done.
How can I best structure this sort of action where I need to display the extent data in a table view and update the data store in the background with new data?
While I am using MagicalRecord for most of it, I am open to comments, answers, etc with or without (straight CD) using MagicalRecord.
So the way I'd handle this is to look at having two managed object contexts each with its own persistent store coordinator. Both of the persistent store coordinators talk to the same persistent store on disk.
This approach is outlined in some detail in Session 211 from WWDC 2013 — "Core Data Performance Optimization and Debugging", which you can get to on Apple's Developer Site for WWDC 2013.
In order to use this approach with MagicalRecord, you will need to look at using the upcoming MagicalRecord 3.0 release, with the ClassicWithBackgroundCoordinatorSQLiteMagicalRecordStack (yes, that name needs work!). It implements the approach outlined in the WWDC session, although you need to be aware that there will be changes needed to your project to support MagicalRecord 3, and that it's also not quite released yet.
Essentially what you end up with is:
1 x Main Thread Context: You use this to populate your UI, and for your fetched results controllers, etc. Don't ever make changes in this context.
1 x Private Queue Context: Make all of your changes using the block-based saved methods — they automatically funnel through this context and save to disk.
I hope that makes sense — definitely watch the WWDC session — they use some great animated diagrams to explain why this approach is faster (and shouldn't block the main thread as much as the approach you're using now).
I'm happy to go into more detail if you need it.

iOS 6 What is the standard way to deal with background saving of managed objects (in 2013)?

Last year I used RestKit 0.10 to seamlessly download and save core data objects in background. However, when I tried to use restkit in 2013, I noticed that they have taken out the ActiveRecord pattern, which I relied upon to abstract away all the unpleasantness of background saving.
I found that the ActiveRecord pattern exists in MagicalRecord framework, but most of the documentation I could find is for version 2.x, while my cocoapods install 3.x.
I spent the last 2 hours searching, and find a lot of answers that are really out of date and no longer work for these new frameworks.
This poses the question: what the standard/easiest way to deal with saving core data objects in background using frameworks available in 2013? Should I try some other framework?
If you don't use any external library like Magical Record or RestKit, but simply go for the all manual stuff, you can take advantage of the new NSManagedObjectContext APIs.
You can now have contexts nested with a parent-child relationship and you can also tell each context to perform a block in it's own thread. My advice, therefore is to have the following structure for your application:
1) A background saving context. This will be the only context that saves and reads data directly to/from the database.
2) A context initalized on the main thread that will be your point of access for everything you need to do in the application, especially updating the UI. This context will be a child of the saving context.
3) As needed, you'll create background contextes that perform work on background threads, e.g. loading data from the network and serialize this data in NSManagedObject instances. This contextes will be children of the main context.
4) Every time you call -[NSManagedObjectContext save:] on a context, you should also call the same method on it's parentContext. To do this you could have a convenience method in a category on NSManagedObjectContext that reads something like this:
- (void)saveSelfAndParent {
[self save:NULL];
[self.parentContext performBlock:^{
[self.parentContext saveSelfAndParent];
}];
}
This is already a thread safe configuration and your changes will propagate the changes up to the database. Note that as the saving context will have no parent (and thus self.parentContext will be nil), the performBlock: won't crash the app.
Here's an example what you need to do to create a new entity assuming you kick off your background work using Grand Central Dispatch (GCD):
dispatch_async(dispatch_async_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSManagedObjectContext *context = [[NSManagedObjectContext alloc] initWithConcurrencyType: NSPrivateQueueConcurrencyType];
context.parentContext = mainContext;
// do some expensive job
...
// initialize a new NSManagedObject instance using the information we calculated
NSManagedObject *myObject = ...;
// once we're done, let's save the context
[context saveSelfAndParent];
});
Note that we initialized the context with a private queue concurrency type (NSPrivateQueueConcurrencyType) which tells the context that he's a background context. That is very important!
That's all! :)
For more information refer to the NSManagedObjectContext Class Reference.

How to avoid the UI freeze while a Managed Object Context is saving?

I want to implement a UI-responsive downloading and parsing of a large data set, saving it with Core Data.
My setup:
I display the downloaded content in a custom view controller. I don't use a NSFetchedResultsController.
There are 3 MOCs:
masterMOC (responsible for saving to disk, NSPrivateQueueConcurrencyType)
mainMOC (used by UI, NSMainQueueConcurrencyType, a child of the masterMOC)
backgroundMOC (responsible for the import from JSON, created in a separate thread, a child of the masterMOC)
I am importing in batches - every 50 items I perform the MOC saving in the following way:
NSError *error;
[backgroundMOC save:&error];
NSManagedObjectContext *masterMOC = backgroundMOC.parentContext; //set during initialization
[masterMOC performBlock:^{
NSError *parentContextError = nil;
[masterMOC save:&parentContextError];
}];
I expect the changes in the mainMOC to be made after the masterMOC is saved. If I try to access some relationship of a random managed object while the masterMOC is saving (saving takes some time), the UI hangs until the saving is completed.
Question: how to avoid the UI freeze while the masterMOC is saving?
Your problem probably is that the data store is blocking while you are writing to it. So, either make the data store non-blocking (this may or may not be possible in your case) or if not viable, make the accessor non-blocking. In the latter case the GUI will not hang, but it also will not update either until the result of the access comes back.

Resources