CoreData: how to leave particular objects in context unsaved and save others? - objective-c

Prerequisites:
I have 2 methods in the network API:
return list of entities (just basic info: name, id, etc.)
return detailed info about entity
The requirement is to save only objects created by processing the second request (save to sqlite) and leave non-full objects without saving.
Also, the 'main' context should contain only full objects from 1st request, and any other 'temporary' context should contain all the others objects.
I've tried to create two instances of NSPersistentStoreCoordinator and use them for different types of contexts, but it seems that for one NSManagedObjectModel can exist only one coordinator (the pointer points to the same adress).

If I understand you correctly, then I think your best option is to only create a managed object once you're sure you want it to persist in Core Data storage. You may need another layer of non-managed objects to contain data for the "non-full" entities
(This would be something like Data Transfer Objects from Java EE programming.)

You can not save indiscriminately from within the same MOC. Saving the MOC always saves everything in it.
So, you can use a separate MOC that is never saved, and then just "move" those objects to the main MOC when they are ready to be saved.

The solution that exactly solves my problem is to create two 'forks' of core data stack:
one with default config and mainContext
the second (new instance of the same NSManagedObjectModel, new
NSPersistentStore (inMemory) and new NSPersistenStoreCoordinator )

Related

Is NSPersistentStoreCoordinator executeRequest without any context, safe to fetch _objectIDs_

We have a background thread that needs to do some fetching.. but it doesnt need any data -- only the objectIDs
originally we did this using a specific newly created blank managed context just for this.
NSFetchRequest *request = [DKDocumentDetails requestAllWithPredicate:predicate inContext:ctx];
[request setResultType:NSManagedObjectIDResultType];
self.objectIDs = [DKDocumentDetails executeFetchRequest:request inContext:ctx];
...
but recently I found out, I can also do this on the PST itself, without any context AS I dont want Managed Objects, but only IDs
NSFetchRequest *request = [DKDocumentDetails requestAllWithPredicate:predicate inContext:mainctx /*used in the wrong thread but only for getting entity description*/];
[request setResultType:NSManagedObjectIDResultType];
NSError *error = nil;
self.objectIDs = [pst executeRequest:request inContext:nil error:&error];
...
so in my tests it never crashed and in the docs I dont see why it shouldnt work either... I mean I dont get unsaved stuff and I cannot get objects, but used this way...
It is faster and looks elegant but is it safe or not?
I've been thinking about your question all day. Here is what I've come up with. As others have pointed out, NSPersistentStoreCoordinator objects are not thread safe. When a bunch of NSManagedObjectContext objects on various threads use the same NSPersistentStoreCoordinator, they do so by locking and unlocking the NSPersistentStoreCoordinator.
However, you are worried about just reading data, and thread safe NSManagedObjectID data at that. Is that ok?
Well, the Apple documentation On Concurrency with Core Data mentions something similar to what you are doing:
For example, you can configure a fetch request to return just object IDs but also include the row data (and update the row cache)—this can be useful if you're just going to pass those object IDs from a background thread to another thread.
Ok, but do we need to lock the Coordinator?
There is typically no need to use locks with managed objects or managed object contexts. However, if you use a single persistent store coordinator shared by multiple contexts and want to perform operations on it (for example, if you want to add a new store), or if you want to aggregate a number of operations in one context together as if a virtual single transaction, you should lock the persistent store coordinator.
That seems to be pretty clear that if you are performing operations on a persistent store from more than one thread, you should lock it.
But wait - these are just read operations, shouldn't they be safe? Well, apparently not:
Core Data does not present a situation where reads are “safe” but changes are “dangerous”—every operation is “dangerous” because every operation has cache coherency effects and can trigger faulting.
Its the cache we need to worry about. That's why you need to lock - a read in one thread can cause data in another thread to get messed up through inadvertent cache changes. Your code never gave you problems because this is probably really rare. But its those edge cases and 1-in-1,000,000 bugs that can do the most damage...
So, is it safe? My answer:
If nothing else is using your persistent store coordinator while you read, yes, you are safe.
If you have anything else using the same persistent store coordinator, then lock it before you get the object IDs.
Using a managed object context means the locking is automatically taken care of for you, so its also a fine possibility, but it looks like you don't need to use it (and I agree it is nice to not make one just to get a few Object IDs).
From the NSPersistentStoreCoordinator docs:
Note that if multiple threads work directly with a coordinator, they need to lock and unlock it explicitly.
I would say that if you were to properly lock the PSC:
[pst lock];
self.objectIDs = [pst executeRequest:request inContext:nil error:&error];
[pst unlock];
That would be considered "safe" according to my reading of the docs. That being said, locking done internally by the MOC might be the most significant performance difference between the two approaches you have described, and if that's the case you might prefer to to just use the blank MOC as it would be less surprising when you or someone else encounter's the code later.
Related question:
Is NSPersistentStoreCoordinator Thread Safe?
There is no a good reason to NOT use a managed object context for this. The managed object context buys you a lot - it handles change management, threading, etc. etc. Using the persistent store coordinator directly loses a lot of this functionality. For example, if you have changes that have not been persisted yet to this store, you may miss them by using the persistent store coordinator directly.
Now you say the reason this is attractive to you is that you only want the managed object IDs. What it seems you really want is to find managed objects but not fire faults on them. You can do this with either a NSManagedObjectResultType or a NSManagedObjectIDResultType on your fetch request. In the case of the NSManagedObjectResultType, you would just access the objectID on your fetched objects, which will not fire a fault - thus not "getting data". This can have some performance advantages if the row cache is already populated, etc.
With all of that said, why not just use parent-child contexts to solve this?

Get a number of resources asynchronously and "asynchronously" save them to a database. Which good pattern to use? (AFNetworking, Core Data)

I need to populate my map with annotations. Each annotation has corresponding Place resource that is being fetched from remote server. Each Place has associated Category - it is fetched from the server too as a separate resource.
Let's assume that to populate a given region I need to fetch 100 places, each belonging to the one of 20 categories (actually there are much more of them).
I use AFNetworking to fetch the both of resources. I try to cache both places and categories for offline use, so before the annotations are displayed on map, I write fetched resources to the Core Data tables.
Each place retrieves its associated category resource by demand and I need to write both a place in the 'places' table, and category in the 'categories' table.
Because of fetching is being done asynchronously, when writing particular category to table I can't know if maybe another place "thread" attempts to write the same associated category to 'categories' table.
So, the question is: what is the pattern for working with Core Data tables, when they need to be populated with information retrieved asynchronously? Specifically how any given thread which is going to write a category could know that there is already one trying to do that?
UPDATE 1: My current problem is that currently I am having the duplication of categories. My guess is that obviously each category which attempts to be written is not aware about the parallel write of the same category.
UPDATE 2: The most simple description of my case is the following:
I create a new Category entity with some fields in one thread and in the meantime in another thread I create exactly the same Category entity with the same fields aiming to be the same Category object like the first thread has.
One thread wins calling [managedObjectContext save:&error], but then before the actual record is appeared in the PersistentStore, the second does call the save too. The question is: how to prevent the duplication of records in 'categories' table?
UPDATE 3: I am considering both variants of using managed object contexts: 1) reusing one shared moc instance by all threads 2) instantiate a new moc on each thread
Thanks!
The "official" answer is going to be something about using an NSOperationQueue and/or taking manual steps to ensure that all your accesses to the NSManagedObjectContext occur on the same thread that created the context. There are a number of references and tutorials that you can follow to implement this approach.
As an alternative, there's a thread-safe Core Data extension on github that will do this for you. If you use it, it will automatically synchronize your database operations so that you don't have to worry about whether or not another thread is doing something with the context. You can just insert things as they come in, and the framework will ensure that your operations are translated into something that won't make Core Data explode.
Full disclosure: I built the github project.

NSManagedObject changed properties after save

Is it possible to find out which properties were saved on a managed object after the save occurs? For example, I have someone listening for managed object context saves, (NSManagedObjectContextDidSaveNotification) and I want to know which properties on the objects were saved.
The NSManagedObjectContextDidSaveNotification does contain all three bits of information you would need to sync with a server. Check the [notification userInfo] and you will find three sets inside: NSInsertedObjectsKey, NSUpdatedObjectsKey, and NSDeletedObjectsKey
If you want to know what properties on an entity have changed that would require that you track them yourself using KVO. I would recommend against this as the odds of that level of effort being worth it over just pushing the entire object up to a server are slim.
Update #2
On further poking around:
From the NSManagedObjectContextWillSaveNotification you could loop through each set and reference the changedValues method. You could keep a reference to that dictionary until after you receive the NSManagedObjectContextDidSaveNotification and then process the changes. Still sounds very heavy to me.
Update
What is your end goal?!?!
If you are trying to figure out what to push to a server then being at the attribute level is too low. You should be syncing at the entity level.
If you are just trying to keep some internal consistency inside of your application then you are thinking way, way too low level. This is a solved problem. Core Data solved it.
Why don't you get them when they are about to be saved. Subscribe to NSManagedObjectContextWillSaveNotification and check insertedObjects, updatedObjects and deletedObjects of the NSManagedObjectContext.
Update:
Even easier, get the user info of the NSManagedObjectContextDidSaveNotification
From the documentation:
Typically, on thread A you register for the managed object context
save notification, NSManagedObjectContextDidSaveNotification. When you
receive the notification, its user info dictionary contains arrays
with the managed objects that were inserted, deleted, and updated on
thread B.
http://developer.apple.com/library/ios/#documentation/cocoa/conceptual/coredata/Articles/cdConcurrency.html#//apple_ref/doc/uid/TP40003385-SW1
Here's the solution I settled with. I have one singleton class that is notified when a context saves. The NSManagedObjectContextWillSave notification tells me which things have changed so I store them in a dictionary with the key being the context that saved. Then when I get the NSManagedObjectContextDidSave notification, I check the dictionary for the associated context. Finally, I remove that entry from the dictionary. Does that seem reasonable?

Restore one RCP view while restoring another

Two views in my application need to load same information when restoring state. My idea was, to avoid saving it twice, to have one view create another in init orcreatePartControl if it wasn't created yet. However,
PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage().showView(...)
doesn't work there, as getActivePage() returns null. Is it possible to work around this?
Delegate to a manager or service to load/maintain/save the shared state. That will ensure the first access initializes your information. When the view is instantiated just go to the manager and retrieve the information. If the user never instantiates your view, then you never had to do the extra work.
In the general case, you can't create/instantiate one view while creating/activating another view. Eclipse won't allow it, and will generate ERRORs in the error log.
EDIT:
3 standard persistence patterns I've seen used (and/or misused :-) are:
1) Have your plugin get its state location and simply serialize you state out there. (location provided for free if you subclass org.eclipse.core.runtime.Plugin) You can do it in your activator stop(BundleContext) method. You can uses classes like org.eclipse.ui.XMLMemento to serialize to/from XML if you don't already have a solution.
2) if you subclass org.eclipse.ui.plugin.AbstractUIPlugin you can use org.eclipse.ui.plugin.AbstractUIPlugin.getDialogSettings() to store your state. Potentially a little bulky as you would have to keep it up to date.
3) have your common manager update a preference, potentially using another serialization technique.

Sharing Non-Persistent Objects Between Contexts in Core Data?

I was wondering if there is a way to share an NSManagedObject between two or more NSManagedObjectContext objects running in the same thread.
I have the following problem: I have one main context shared through all my code in the application and several different contexts that are created for each remote fetch request that I issue. (I created a custom class that fetches remotely and inserts all the objects found in the server in his own NSManagedObjectContext). Those fetch requests may run simultaneously since they use NSURLConnection objects that may end at different times. If the same remote object gets fetched by different connections, I will end up with duplicates at the moment of saving and merging the context with the main one. (That is, objects that have the same remote ID but a different objectID).
One possible solution would be to save (and so persist) every object as soon as it is created but I can't do that because it may have some relationships that may still have not been filled and won't validate during the save operation.
I'm really looking forward to a method that allows you to share the same non-persistent instance of an object between context. If anybody has encountered this issue and came up with a solution, I would be pleased to know!
Context cannot communicate between each other save through their stores. However, you can insert a managed object with a nil managed object context and it will be independent (albeit without relationships) of any context. You could pass that independent managed object around however you wished and insert it into a context when you needed to persist it. This is dangerous but possible.
However, if you're not running each connection on a separate thread then you don't gain anything by having multiple context. Each connection object will activate its delegate in sequence on the main thread. In this case, your easiest solution would be to use the same delegate for all the connections and let the delegate handle the insertions into a single context. To prevent duplication, just do a fetch on the remoteID and see if you get back an extant object before inserting a new object for that remoteID.
I don't think what you want to do is possible. I mean if you want to share changes between different contexts, you got to use notifications and merge it whenever did save or did change occur. But in your case, I'd say just use 1 context and save in the end. Or a less elegant way: save all the remote ids temporary in your app and check before inserting new ones. In this case, you can continue use multiple contexts and save after each didfinishloading.