MagicalRecord MR_saveOnlySelfAndWait doesn't work within saveWithBlock - ios

I'm getting the grips with MagicalRecord 2.3 and it seems as though if I run MR_saveOnlySelfAndWait within a saveWithBlock, nothing persists (the callback has a success flag of NO and error is nil).
However, when I run MR_saveToPersistentStoreAndWait within that block it works no problem. The object in question only has one attribute (a String) and it's set.
Is this due to the fact that saveWithBlock runs on a background thread anyway? Pretty confused here.

MagicalRecord is designed to work with nested contexts. Te rule with regard to nested contexts is that if a context does not have a parent, then calling save will persist changes to the store. If there is a parent, then a save will only persist changes to the parent, and not to the store. This is the reasoning behind the two methods. Choose the proper save for your situations

Related

ViewContext not receiving updates from newBackgroundContext()

There is a similar question in stack overflow already but it doesn't work for me.
There is a use case in my application where I have to observe the database changes to perform some operation. To receive updates I subscribed to NSManagedObjectContextObjectsDidChange notification (for ViewContext) and also I turned on automaticallyMergesChangesFromParent.
But, if I update or delete the object on some other context (using newBackgroundContext()), I don’t receive object did change notification but it’s working perfectly for inserting new objects.
Can you someone please guide me why it does work only for insert, not for update and delete even after enabling automaticallyMergesChangesFromParent? if it's the actual behavior, Is there any other way to solve my use case?
The documentation (in NSManagedObjectContext.h) for .automaticallyMergesChangesFromParent says:
Whether the context automatically merges changes saved to its
coordinator or parent context. Setting this property to YES when the
context is pinned to a non-current query generation is not supported.
What I tried
I debugged by testing if updated/deleted objects are already
registered in the view context. Yes, those are already registered.
I tested the same thing using NSFetchResultController it’s working
good but unfortunately, I can’t use NSFetchResultController as I
use a custom view to represent the data
Also, I tried creating a new privateQueueConcurrencyType context and setting viewContext as a parent and it surprisingly started working so the issue is only while using newBackgroundContext() but as per document it should work properly as both are using same NSPersistentStoreCoordinator
Thanks in advance!
"I can’t use NSFetchResultController as I use a custom view to represent the data" not true, the FRC can and should be used with any view (that shows multiple objects).
As for why you are not receiving the NSManagedObjectContextObjectsDidChange in the case of updates (which come in as refreshed) or deletes I have a few theories:
Maybe not properly called _persistentContainer.viewContext.automaticallyMergesChangesFromParent = YES; because that causes exactly the situation you describe (receiving inserts but not updates or deletes). It should be done in the persistentContainer custom getter in your app delegate after loadPersistentStoresWithCompletionHandler (and not inside the block).
Perhaps not using performBlock with your background context.
Possibly not registered for the did change notification correctly.
(In light of new information) Not retaining the fetched objects.
If you would like to share some code we can help you track down the bug.
Probably not totally an answer but just some thoughts and suggestions that are not well enough structured for a comment.
It could be related to the viewContext not retaining the objects, rather just faults because they weren't being used anymore directly (such as in a tableview). There would be retainsRegisteredObjects for that.
Also, did you ever access the property that has been changed in the viewContext? That could also be an issue, that it wont recognize a change on an object that never got read.
I strongly recommend using one or multiple FRC for those cases, they bridge the notifications for you and provide a cleaner interface. It does not matter if you have a custom view, just implement the FRCDelegate methods and you will be fine. I think it could be easier to help you if you ask another question why you cannot use FRC (where are the problems?) with your custom view.

CoreData - Making Objects That May Or May Not Be Stored

I have an existing iOS project which I'm trying to change to use CoreData instead of Keyed Archiving. In my existing project, I often create objects when the user closes a modal view. Then I do some validation on that object, and if all is well I go ahead and store it. But if the object is determined to be invalid, I simply throw it away and throw up a validation message to the user.
I got nervous about this pattern when I started using Core Data. I didn't want to keep creating "temporary" objects in my database only to turn around and delete them. So instead I create the objects without assigning them to a NSManagedObjectContext. Then later when I determine things are valid, only then do I assign it to a context.
At last here's the question: Is this a valid approach? Are things commonly done this way? I'm worried as my use of managed objects gets more complicated, with more relationships between entities, that I may regret this choice. Am I unnecessarily making things harder for myself?
A common way to do this is to create a child conteit of the main context. You can create your objects in that context, and if you want to save the changes to the main context and then save the main context. If you don’t want to keep the changes just throw away the child context.

Magical Record with concurrency

I'm in the middle of development of an iOS application after working quite some time with Core Data and Magical Record and having an error:
error: NULL _cd_rawData but the object is not being turned into a fault
I didn't know Core Data before this project and as it turns out I was very naive to think I can just work with Magical Record without worrying about concurrency, as I haven't dedicated any thoughts/work about managed contexts for the main thread and background threads.
After A LOT of reading about Core Data Managed Object Contexts and Magical Record, I understand that:
NSManagedObjects are not thread safe.
NSManagedObjectId IS thread safe.
I can use: Entity *localEntity = [entity MR_inContext:localContext] of Magical Record to work with an entity in a background thread's context.
I should use Magical Record's saveWithBlock:completion: and saveWithBlockAndWait: methods to get a managed context to use for background threads.
A little information regarding my application:
I'm using the latest version of Magical Record which is 2.2.
I have a backend server which my application talks to a lot.
Their communication is similar to Whatsapp, as it uses background threads for communicating with the server and updating managed objects upon successful responses.
I'm wrapping the model with DataModel objects that hold the managed objects in arrays for quick referencing of UI/background use.
Now - my questions are:
Should I fetch only from the UI thread? Is it okay that I'm holding the managed objects in DataModel objects?
How can I create a new entity from a background thread and use the newly created entity in the DataModel objects?
Is there a best design scenario that I should use? specifically when sending a request to the server and getting a response, should I create a new managed context and use it throughout the thread's activity?
Let me know if everything is clear. If not, I'll try and add clarity.
Any help or guidelines would be appreciated.
Thanks!
I'm not working with MagicalRecord, but these questions are more related to CoreData than to MagicalRecord, so I'll try to answer them :).
1) Fetching from main(UI) thread
There are many ways how to design app model, so two important things I've learned using CoreData for few years:
when dealing with UI, always fetch objects on main thread. As You correctly stated NSManagedObjects are not thread safe so you can't (well, can, but shouldn't) access their data from different thread. NSFetchedResultsController is Your best friend when you need to display long lists (eg. for messages – but watch out for request's batchSize).
you should design your storage and fetches to be fast and responsive. Use indexes, fetch only needed properties, prefetch relationships etc.
on the other hand if you need to fetch from large amount of data, you can use context on different thread and transfer NSManagedObjectIDs only. Let's say your user has huge amount of messages and you want to show him latest 10 from specific contact. You can create background context (private concurrency), fetch these 10 message IDs (NSManagedObjectIDResultType), store them in array (or any other suitable format for you), return them to your main thread and fetch those IDs only. Note that this approach speed things up if fetch takes long because of predicate/sortDescriptor not if the "problem" is in the process of turning faults into objects (eg. large UIImage stored in transformable attribute :) )
2) Creating entity in background
You can create the object in background context, store it's NSManagedObjectID after you save context (object has only temporary ID before save) and send it back to your main thread, where you can perform fetch by ID and get the object in your main context.
3) Working with background context
I don't know if it's exactly the best, but I'm pretty much satisfied with NSManagedObjectContext observation and merging from notifications. Checkout:
mergeChangesFromContextDidSaveNotification:
So, you create background context, add main context as observer for changes (NSManagedObjectContextObjectsDidChangeNotification) and background context automatically sends you notifications (every time you perform save) about all of it's changes – inserted/updated/deleted objects (no worries, you can just merge it by calling mergeChangesFromContextDidSaveNotification:). This has many advantages, such as:
everything is updated automatically (every object you have fetched in "observing context" gets updated/deleted)
every merge runs in memory (no fetches, no persisting on main thread)
if you implement NSFetchedResultsController's delegate method, everything updates automatically (not exactly everything – see below)
On the other side:
take care about merging policy (NSMangedObjectContext mergePolicy)
watchout for referencing managed objects that got deleted from background (or just another context)
NSFetchedResultsController updates only on changes to "direct" attributes (checkout this SO question)
Well, I hope it answers your questions. If everything comes up, don't hesitate to ask :)
Side note about child contexts
Also take a peek to child contexts. They can be powerful too. Basically every child context sends it's changes to parent context on save (in case of "base" context (no parent context), it sends it's changes to persistent coordinator).
For example when you are creating edit/add controller you can create child context from your main context and perform all changes in it. When user decides to cancel the operation, you just destroy (remove reference) the child context and no changes will be stored. If user decides to accept changes he/she made, save the child context and destroy it. By saving child context all changes are propagated to it's parent store (in this example your main context). Just be sure to also save the parent context (at some point) to persist these changes (save: method doesn't do the bubbling). Checkout documentation of managing parent store.
Happy coding!

EXC_BAD_ACCESS on mergeChangesFromContextDidSaveNotification

We have been trying to debug a Core Data multiple-context/threading issue wherein merging a Core Data save notification into our main thread NSManagedObjectContext is sporadically crashing the app. This is crashing ~2% of our app sessions and we are at a loss as to how to solve this. We would really appreciate any guidance or general advice on what could possibly cause this crash.
We have a Core Data setup that looks like this:
N.B. This is the default Core Data stack in Magical Record v2.3 created from [MagicalRecord setupAutoMigratingCoreDataStack]
This is the scenario where our app is crashing:
HTTP request returns JSON
JSON is parsed into NSManagedObjects (Some new entities, some updated entities) on Root Saving Context
Root Saving Context saves to persistent store
NSManagedObjectContextDidSaveNotification is broadcast by Core Data. Default context on main queue observes this and calls mergeChangesFromContextDidSaveNotification: with the NSDictionary of changes on the main thread.
It crashes when objectID is sent to an invalid object (most likely NSManagedObject has been deallocated).
This is occurring inside the private implementation of NSManagedObjectContext mergeChangesFromContextDidSaveNotification: so it is impossible for us to see what has actually gone wrong here; all we can tell at this point that an object which should exist, does not.
This only happens on a small percent of Core Data saves, indicating that may not be a fundamental flaw in our Core Data → API stack. Moreover, there is no indication that the size or type of the changes (insertions/updates/deletions) in the context changes have any impact on the likelihood of the crash.
The documentation of NSManagedObjectContextDidSaveNotification says that:
"You can pass the notification object to mergeChangesFromContextDidSaveNotification: on another thread, however you must not use the managed object in the user info dictionary directly on another thread. For more details, see Concurrency with Core Data in Core Data Programming Guide."
Maybe this is the issue? I would make sure the object u get from the notification is getting saved on Default Context on the same thread it was posted by Root.
It's been some time now since this question was posted and after rediscovering it I'd like to answer my own question for the sake of others who find this thread.
In my circumstance I had migrated a large code base from sibling NSManagedObjectContexts updated via NSManagedObjectContextDidSaveNotification's. However the problem was not really anything to do with this, even though this did expose the issue.
The real cause of this were places that there were older parts of the code, setup by previous engineers, that had setup KVO on NSManagedObjects and their properties. It transpired that KVO on Core Data entities is in fact a very very bad idea.
More accurately, it appeared that this happened when KVO was setup on entities and either the object, or the target of a relationship on this object was deleted from the NSPersistentStore. This second condition seemed to not be the only cause of the issue, but was definitely a very prominent cause in my situation.
Lesson's learnt:
Use a fetched results controller when you need to. KVO is not a convenient shortcut and you shouldn't avoid migrating dodgy Core Data KVO code to NSFetchedResultsControllers or another sensible alternative as the procrastination will just hurt you.
Multi threaded Core Data is a difficult but very worthwhile skill to become an expert in. Knowing your Core Data stack and the nuances and limitations of Core Data multithreading is absolutely worth all the mental anguish.
One possibility is that your persistent store has become corrupted and is in an inconsistent state. If this happens an error code is generated which Magical Record does not necessarily deal with. This can be the source of a number of difficult-to-repeat apparently-random crashes related to Magical Record (and may or may not be considered a Magical Record bug).
It's worth reading the Magical Record issues threads here (same issue) and here (different issue, but could be similar cause). When I hit these problems I managed to make some temporary patch fixes following various hints in those threads, but ultimately I decided to remove my dependency on Magical Record, and I have had no problems since then.

unexplained behavioural difference between objectWithID: versus existingObjectWithID:

I understand the documented differences between these two calls. However does anyone know the reasons for the following observed behaviour I have noticed:
If I have a parentContext and a temporary childContext where I use the childContext to edit, insert and deleted objects, if use [childContext objectWithID:objectID]; to retrieve a known existing managed object, present in the parent context, it will sometimes give me an object with a fault that on being fired fails and generates an exception. I understand objectWithID: will, by design, always return an object in a faulted state regardless of if an actual managedObject exists for the given objectID. However if the object actually exists in the parent context, I would expect that when any of the properties are accessed, the object will always be successfully retrieved from the parent context (e.g. the fault will be fired) without any problem. If I use [childContext existingObjectWithID:objectID]; I find it does indeed always succeed.
For the record, I have turned off caching on the child context and this same behaviour occurs after [childContext resetContext] has been called - so it's not an artefact of old cached data hanging around that is inconsistent with the parent context.
The documentation alone seems to me to be insufficient to explain this behaviour. I can of course chalk it up to experience and just say "I now know to always use existingObjectWithID: when passing object IDs to my child edit context perform block" but I feel uneasy and would like to understand exactly what is going on here (not least so I can understand if there is any performance impact of using the one as over the other but also to understand what the constraint is so I can ensure there isn't some bad practice I'm unnecessarily implementing in my code and then using a wrong or inefficient call to fix it).
I've seen this behavior in my own code (the stack traces were from Mars, took forever to track down the cause of the issue), and my solution was the same (move from use of objectWithID: in the child context to use of existingObjectWithID:).
In my case, I'd create an object on the parent context, immediately obtain a permanent object ID for it, and ask for a save using UIManagedDocument's updateChangeCount:UIDocumentChangeDone, which will schedule a save at some point in the future. I think this point is key for the discussion.
I'd then perform a network call to obtain XML data pertaining to the newly-created object, and parse and import that data into Core Data. This occurred on a background thread, and I'd pass the object ID to the thread, which used a thread-confined child import context.
Under iOS5, I'd use objectWithID: in the worker thread to fault the new object into the import context. Worked fine, no issues.
Under iOS6, this began to fail, with bizarre stack traces, and the only solution was to move to existingObjectWithID:. Under testing, it appears to be a solid solution with this change in place.
Like you, I've tried to find some definitive statement as to why this works, but I've been unsuccessful in doing so. However, I think the pattern that my code exhibited and the stack traces I was seeing perhaps explains what's happening. I was always seeing a crash attempting to obtain data from the persistent store. I believe that under iOS5, the save I requested via UIManagedDocument was occurring before my child thread got a chance to run, and thus the new object was persisted before I called objectWithID:. It would appear from my crash logs that that is not the case under iOS6; the thread runs before the object has made it to the persistent store, and thus it can't be fetched into the child context yet. My assumption is that existingObjectWithID: is ensuring that any pending SQL I/O is performed before attempting a fetch, and thus the persistent store is consistent when the fetch occurs in my worker thread. I have been unable to find anything definitive to support this, but extensive testing seems to support that that's what's happening.
Having the same issue as the OP, and while I think the exception is by design as per
If the object is not registered in the context, it may be fetched or returned as a fault. This method always returns an object. The data in the persistent store represented by objectID is assumed to exist—if it does not, the returned object throws an exception when you access any property (that is, when the fault is fired). The benefit of this behavior is that it allows you to create and use faults, then create the underlying data later or in a separate context.
in the Apple docs -- that is, the data is NOT in the persistent store yet, it's sitting in the parent context -- it still seems odd that I simply cannot get my realized object in the parent context. Why must the persistent store have to know about it, for my object to be populated?
Getting the "existing object" using existingObjectWithID before getting the "crash object" using objectWithID will lead to both objects being equal.
The other way round the app will crash as soon as a property of the "crash object" is accessed. In this case the objects are not equal. The "crash object" has a temporary ObjectID.

Resources