1

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:

  1. Should I fetch only from the UI thread? Is it okay that I'm holding the managed objects in DataModel objects?
  2. How can I create a new entity from a background thread and use the newly created entity in the DataModel objects?
  3. 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!

Chen Avnery
  • 110
  • 2
  • 9

1 Answers1

8

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!

Community
  • 1
  • 1
JakubKnejzlik
  • 6,363
  • 3
  • 40
  • 41
  • Wow, I couldn't ask for more with this explanation. Thanks a lot! If you're at it, I do have one more question: What is the best practice in your opinion for the UI to be working with CoreData/MagicalRecord elements? I'm using a layer in the middle which I call it DataModel which the UI talks to and it talks to CoreData elements and the backend server. Now I'm thinking I should maybe change my design because what you suggested about the Add/Edit UI pages which require a new managed context. Any suggestions? – Chen Avnery May 28 '15 at 09:13
  • Oh and BTW, why don't you use Magical Record, if I may ask? – Chen Avnery May 28 '15 at 09:32
  • Well, the main reason could be "I haven't switched yet and I'm lazy to do so" :D. Few years ago I made mistake and thought that MagicalRecord is based on ActiveRecord pattern (which is IMO very bad for CoreData). I've created my own little helper (see for more info https://github.com/jakubknejzlik/GNContextManager). Now I'm a little bit busy to learn enough to switch to MR. But it's great tool with lots of features anyway :) – JakubKnejzlik May 28 '15 at 11:05
  • Thanks a lot for your help. It helps immensely. You obviously have way more experience with programming for iOS than me, so let me ask you this - Let's say the project is to program the Whatsapp iOS client. Is that considered a complex project? would you use a mediator and an api adapter for it like you described? – Chen Avnery May 28 '15 at 11:26
  • 1
    Thanks for compliment, but I still have a lot of learning ahead :). I think it's always better to split logic into several segments. The key point IMO is to stick sections together. So if you have things for communication with server about stuff A create AAdapter, for server B create BAdapter. If your app can print, create PrintManager and for each printer create adapters etc. – JakubKnejzlik May 28 '15 at 13:59
  • OK, last question (promise): How do you manage all of the Managed Object Contexts with all of these sections? who is creating the contexts and passing them? who is responsible for saving and fetching? – Chen Avnery May 28 '15 at 14:03
  • 1
    I recommend checking out Design Patterns from Gang of Four. They define patterns that can be used and also provide usage description and examples. I'm not using all of them and didn't read the whole book, but it helped to see things in different angle :) – JakubKnejzlik May 28 '15 at 14:05
  • I think it's not important who's creating which context. More important is who else (what class) has access to context you created in specific class. You can have as many contexts as your memory can hold, but the key is how to share contexts between classes. In one class you handle saves/updates/deletions/insertions in logic of this class, but when this context is used by other classes, things can get messy. I rather share managedObjects and use it's .managedObjectContext – JakubKnejzlik May 28 '15 at 14:13
  • @JakubKnejzlik I realize this is an old thread, but you were correct in thinking that MagicalRecord is based on the ActiveRecord pattern. Would you mind explaining why you think ActiveRecord is not a good fit for CoreData? It seems to me like CoreData is itself a version of the ActiveRecord pattern. – T Blank May 03 '16 at 01:02
  • I think the biggest problem with raw AR pattern is nicely described in second article on wiki `Thus, an object instance is tied to a single row in the table.`. and the lack of contexts and other stuff makes it very inconvenient for use in more complex use cases. For another pros/cons checkout for example this article: http://www.mehdi-khalili.com/orm-anti-patterns-part-1-active-record/ – JakubKnejzlik May 03 '16 at 01:33