Would this fill the child context with only TemporaryIDs? - ios

I was wondering if someone would be able to answer this question. I am currently building my application using the CoreData stack as described by Marcus Zarra in his blog http://martiancraft.com/blog/2015/03/core-data-stack/. He describes the usage of managedObjectContext as the Single Source Of Truth where all insertions/updates/deletions should be done on this context. No exceptions. Curiously then, since this context is a child context to the private context, if I keep inserting new NSManagedObjects into managedObjectContext... wouldn't this context be filled with temporaryObjectIDs since the parent does not refresh the child context? I ask this question because how would I retrieve this NSManagedObject back from the PSC using the NSManagedObjectID if I only have the temporary one? Would I have to explicitly throw out another performBlock using the privateContext to fetch for it? That feels like a very inelegant of a solution.

You can force the conversion to happen early, before you've saved changes, like so:
[moc obtainPermanentIDsForObjects:moc.insertedObjects.allObjects error:&error];
As I understand it, the object is already in both contexts. You don't need to retrieve it from the store, as it's cached.
You still have the object and could query the store, based on the object's attributes. The only need for the ID was if you needed to pass it to another thread, but you only have the one Single Source Of Truth. What other thread is there?
Update:
After discussing your app, I would suggest not to add complexity if there is no need for complexity. You already have an API thread. Don't try to prematurely optimize it by splitting an API task into multithreaded sub tasks. You're adding overhead and the unnecessary burden of passing IDs between private contexts.
Treat what the background API is doing as a single operation, and let it download, insert, save, then notify the SSoT, which can then (re)fetch.
If it is responsive enough, you're done. You have a simple, contained approach, with no need to pass IDs.
In general, don't optimize unless there's a need for it.

Related

Realm Swift: passing unpersisted objects between threads

I have a model some instances of which I need to persist. Only some, not all, since persisting all instances would be wasteful. The model has primaryKey of type Int
I need to be able to pass all objects from background to main thread since Realm objects can only be used by the thread on which they were created. Current version of Realm Swift (0.94) does not seem to support handing the object over to another thread directly.
For persisted objects (the ones saved to storage with write) this is not a problem, I can fetch the object on another thread by primaryKey.
Unpersisted objects, however, are problematic. When I create a new object with the same primaryKey in background (I suppose it should be treated as the same object since it has the same primaryKey) and try to fetch it on the main thread (without persisting changes with write since I don't want in in the storage), I seem to get the old object.
I see the following solutions to this problem:
1) persist all objects (which is undesirable and otherwise unnecessary)
2) keep separate model for the objects I want to persist (leads to code duplication).
3) use init(value: AnyObject) initializer which creates unpersisted object from a Dictionary<String, AnyObject> (would probably require manual copying of object properties to dictionary, tedious and potentially error-prone)
Are there any better solutions?
Offtopic: I haven't tried Realm for Android, is situation any better with it?
You are free to pass unpersisted objects between threads as you wish -- only persisted objects cannot yet be handed over between different threads.
I think your problem is that you are creating two objects that you want to be the same object and there is no way the system can know which one you want.
The solution is as simple as it is generic: create a new object only after checking that its unique attribute does not exist already. This should work equally well with persistent and non persistent objects. Obviously, you need to have a central, thread safe in-memory repository where you can go and create new objects.
You write:
I seem to get the old object.
There should not be any old object if you have checked before.

Core Data--pass name of entity or reference to entire entity?

I'm making a simple bank account tracker, for self-instructional purposes. I'm using Core Data to store three entities, related as in the screenshot:
WMMGTransaction objects are simply stored as they are recorded, and extracted as needed to feed tableviews and detail views. This will be done via NSFetchedResultsController and a predicate. I'm using MagicalRecord to access Core Data, if that matters.
My question is this:
When I pass WMMGAccount data from one VC to another, such as when creating a new account, or when selecting one from a list (via delegation as a rule), does it matter if I pass a reference to the entire entity, or can I just use an NSString bearing the .name of the account and identify the account when required with a predicate and an NSFetchedResultsController? I guess this is a strategy question, and may generate discussion, rather than having a cut and dried answer, but I'm wrestling with it, so I thought I'd ask.
It sounds like you're asking if you should pass an object to the code that needs it, or if you should pass information that could be used to look up the same object again.
Unless you need to use the managed object on a different thread or queue, you should always pass the actual object. No sense re-fetching an object you already have. It's extra work and code complexity that (unless there are some unusual extenuating details you didn't mention) won't help in any way.
If you are needing to use the object on a different queue or thread, passing information that can be used to look it up is the correct approach. But in that case-- don't pass the value of one of the properties. Use the managed object ID.
Core Data won't force name values to be unique, while the object's managedObjectID is unique. It's also faster when retrieving the object, because you can use objectForID: or existingObjectForID: instead of performing a fetch.

Core Data multiple context uniqueness

I am using the core data stack shown in the image below. I want to design a structure where objects can be created in both worker contexts.
What I am observing in the setup is if both contexts try to create the same object (for a unique key) at around the same time, db ends up in creating two rows for the table. Is there a way to solve this? Thanks in advance for your response.
The only way you can ensure uniqueness would be to have a coordinating object that all contexts turn to to verify their operation (a "uniqueness enforcer" if you will).
The general algorithm is described HERE, however you fall under the "multi-threaded/context" category and this will complicate things.
In a multi-threaded environment, your enforcer would have to perform a save to the store (using its own managed object context) before returning results to the calling object.
The general flow would be (no cache version):
A context request object for keys from the enforcer
The enforcer issue the request "under lock" (either locking an actual lock or using a serial dispatch queue)
the enforcer query the store for existing objects
create objects for missing keys and save them
you might want to mark the objects as stubs, as the caller might not eventually save and it will give you a flag to ignore them in your fetch requests in your views
build the results array with the objects he created
the results might be NSManagedObjectIDs or imported objects in the caller context otherwise you risk cross context access of managed objects

Refetching object when traversing relationship in Core Data

In our app, we make use of multiple NSManagedObjectContext instances, one per thread created. So when I'm about to use any object, I always make sure that I got the object from the correct context by doing this.
object = (ObjectClass *)[[contextProvider contextForCurrentThread] objectWithID:[user objectID]];
Since I want to make sure the object is updated I go and refresh it:
[[contextProvider contextForCurrentThread] refreshObject:object mergeChanges:YES];
And my question is: If I want to traverse relationships from this object, i.e object.someRelationship, Should I also make sure that the relationship is in the current context, and refresh it? What would be the correct approach?. Refetching and refreshing every time is such a pain...
Look into nested contexts and concurrency types. They won't solve your underlying problem but they are much better than what you already have.
You need policies. To me it seems that your software (without knowing it at all) is lacking structure. Structure comes with policies.
Even if you were to refresh your objects (all of them) all the time you have serious race conditions.
Without knowing the details of your app nobody will be able to suggest anything except "think about what you do" and "introduce policies".

Core Data: Turning Objects into Faults after saving them?

In my code I do a large import of thousands of objects and execute save after each object .
Do I need to turn each object into faults after saving it to save memory or will Core Data turn it automatically into faults? Unfortunately, I have not found any clue in Apple`s doc.
Thank you!
A. You can turn an object into a fault with refreshObject:mergeChanges: (NSManagedObject). This will give up strong references to related objects, so that they can be released. (If they are not holded by another reference.)
You can turn a realized object into a fault with the
refreshObject:mergeChanges: method. If you pass NO as the mergeChanges
argument, you must be sure that there are no changes to that object’s
relationships. If there are, and you then save the context, you will
introduce referential integrity problems to the persistent store.
(Link)
B. You can wipe all objects out with -reset (NSManagedObjectContext) as Daniel G said. But this really wipes out the objects, references can break.
C. I think, that there is no promise of CD to turn all saved objects into faults. (Unsaved objects cannot turn into faults.) But simply overwrite -didTurnIntoFault (NSManagedObject) to see the behavior of CD.
I'm not exactly sure what you are asking but if you have a lot of objects hanging around in your context you can always use the method -[NSManagedObjectContext reset] to reset the context and purge memory. You also don't want to do this if you have any objects that reference NSManagedObjects within the recently reset context.
I'm not sure as to the nature of your application or why you would need to execute a save on thousands of objects, it seems that you should look into using batches for your fetch requests. This way core data will guarantee that only a specified number of objects will be living in the context at any given time.
I hope this helps?

Resources