Am I using Core Data properly by fetching an object once at startup and calling save when I want to persist the data? - iphone

I'm diving into iOS programming and am using Core Data in my app to persist the game data, however, I'm wondering if my approach is the wrong way to use Core Data. I have three tables in my DB, with the first two having a one-to-many relationship with the another table (i.e. UserProfile -->> Puzzle Packs -->> Puzzles).
The approach I'm taking to use and persist the data is simple, retrieve an instance of a UserProfile using a NSFetchedResultsController and store the UserProfile object as an instance var in my App Delegate. Then I use that UserProfile object throughout the rest of the code to access and modify the state of the puzzle packs and puzzles (representing the user's progress in the game) and whenever I make a change to the objects, I just call the NSManagedObjectContext's save method to update the DB, which is is also stored in the App Delegate.
My question is, should I be fetching data from the DB anytime I need to access or modify it or is my current approach, of fetching the top object once and calling the save method often, the correct way to use Core Data?
Thanks in advance for your wisdom! I apologize if my question is odd, I'm still a noob.

As long as you're only ever working with these objects with a single NSManagedObjectContext (ie, you never have to merge changes between contexts) then I don't see a problem with fetching once, and saving as needed.
That said, the moment you work with multiple contexts (for example, using NSOperation/NSOperationQueue or other multi-threading techniques, which require separate contexts), you'll want to make sure you merge changes and update other contexts so changes from one thread don't clobber something on another.

Related

Open UIManagedDocument take too much time

Recently, I'm working with a timetable app in iOS, and i get trouble with Core Data.
My app has a main user interface kind of like the original calendar app created by Apple, and i save all my events data in the Core Data database.
I create a UIManagedDocument in order to fetch data from database by using its NSManagedObjectContext, and everything works just fine.
However, i need to use the NSManagedObjectContext to fetch data several times in several different view controllers during the runtime. And every time i do this, i need to reopen the UIManagedDocument, but open the document take too much time(it may take 2 seconds or even more, i have to display a spinner in view).
So here are my questions:
What's the right way to open a managedDocument?(I mean like open it during the lunch image time?)
Is there a way to only open the managedDocument once and keep it open during runtime?(So i can use its context all the time)
Does data store in the managedDocument i create?(I found that if i delete the document, data was gone)
Thanks.
You will get lots of different opinions on how to do this but basically you should only have to open the document once. This is done by some object that does the work and stores it so it can return it again when asked by a different view controller.
Many people create singleton's for this or put it in the App Delegate. I have implemented a protocol that lets me put it where ever it is convenient for a particular application without my other code having to know anything about the object that returns the information. As long as it responds to the protocol it can be the App Delegate, a singleton class, or any other object type.
See importing AppDelegate
The protocol that I put in the above example just returns information about where the database is. In my actual implementation I have an openDatabase method with a call back to let me know when it is done plus automatic initialization and updating methods.
You can also improve your performance by having the open operation happen off the main thread. This keeps your UI responsive but does not show the data any faster and you have to be carefull about managed object contexts and the threads they are in.
Good luck...

CoreData relationships are nil after mergeChangesFromContextDidSaveNotification

Having some truly strange behavior with Core Data on iOS.
I have a NSManagedObjectContext for my main thread used to read data from a SQLLite persistent store and display it to the user. I also have background processes managed by an NSOperationQueue. These background processes create an NSMangedObjectContext, fetch data from remote servers, and persist that data to the local Core Data store.
I have registered for NSManagedObjectContextDidSaveNotification and when I receive those notifications I call mergeChangesFromContextDidSaveNotification on the main thread's NSManagedObjectContext (passing the notification object as an argument).
This is all very standard and the way that all the Core Data document suggest that you deal with multi-threading.
Up until recently, I have always been inserting new objects into the data store and not modifying objects in the data store. This worked fine. After a background thread writes new data, the merge occurs, I send a notification to the UIController and redraw my display. The display draws correctly.
Recently I have made a change, and the background thread is both inserting and modifying objects. But all the rest of the pattern remains the same. Now after the merge, the data in my main thread’s NSManagedObjectContext is corrupted. If I try to query for objects, I get nothing back. If I try to examine objects which I already have a reference to all their relationships are nil (not faults but nil). I have checked the SQLLite database and the data is all there.
The only solution seems to be to reset the NSManagedObjectContext which isn’t acceptable given the architecture of the applicaiton.
Ok, a final bit of strangeness. If my background thread is only updating attributes (primitves) then I don’t get this strange behavior. However, if I update the relationships themselves, then I get these empty fetch request results and nil’d out relationships.
What am I missing?
Ok, I figured out my problem.
My problem is the way that mergeChangesFromContextDidSaveNotification merges changes from one thread to another. As I have read several times, it does not do a playback of the changes you made but rather merges the final state together. I didn't realize the ramifications of this. In my background thread, I am deleting an object, but before I delete it I nil out some of its relationships because they are marked as cascade delete and I don't want them deleted.
Apparently when I merge those deleted objects into the main context, Core Data still enforces the delete rules and cascade deletes the related objects in the main context.
I changed my delete rules and now my relationships is not getting incorrectly nil'd out. It is unfortunate that I have a relationship which in some cases I want cascade deleted and in others I don't. I will have to work on that.

Can I make instances of an NSManagedObject in no particular NSManagedObjectContext?

I'm building an app which receives a number of listings from a web API, and allows the user to save some for offline viewing. My usual approach would be:
Get the data from the API, and make a new Listing object for each datum
Save the object to the DB if the user chooses to do so.
But this is a Core Data app, so the context is what gets saved, not the object. Under those circumstances, the above would become something like this:
Get the data from the API, and make an unmanaged Listing object for each datum
Move the object into the managed context if the user chooses to do so, then save the context
One approach to having an unmanaged and a managed version of Listing objects would be to have two classes, e.g. ManagedListing and UnmanagedListing - but that's a horribly repetitive way of doing it.
What I'd like is to make Listing a subclass of NSManagedObject; initialise a bunch of them without an NSManagedObjectContext; then when I want to save one, I either set its context or I copy its attributes to a new Listing inside a context.
Can I make instances of an NSManagedObject in no particular NSManagedObjectContext? If so, how?
Use two persistent stores, one in memory and one on disk. If the user wants to save, move the object to the other store using assignObject:toPersistentStore:.
In iOS < 5.0 - yes.
In iOS >= 5.0 - no.

Core Data with only one Data Context. Is it right?

I'm trying to make my first application using Objective C + Core Data, but I'm not sure it's the correct way, as it feels really weird to me.
I have only one data context, which I create at launch time, in the Application Delegate. This data context is used for all the operations (read, write). In another environment (C# and LINQ for example), I try to make these operations as unitary as possible. Here it seems I just have to create the data context once, and work with it without closing it ever (except when the application exits).
I also have an asynchronous operation in which I update this data. Of course, it uses the same data context again. It works, but doesn't feel right.
My Application Delegate keeps a NSArray of the objects contained in Core Data. I use this same NSArray in all my views.
I would actually naturally close the data context once I got all the objects I require, but... aren't the objects always attached to the data context? If I close or release the data context, all these objects will get releases as well, right?
As you can notice, there is something I'm missing here :) Thanks for your help.
The NSManagedObjectContext to which you refer is more of a "scratchpad" than a database connection. Objects are created, amended, destroyed in this working area, and only persisted ("written to the database" if you prefer) when you tell the MOC to save state. You can (and should) init and release MOCs if you are working in separate threads, but the App Delegate makes a MOC available so that all code executing on the main thread can use the same context. This is both convenient, and saves you from having to ensure that multiple MOCs are kept in sync with each other.
By keeping an NSArray of Core Data objects, you are in effect duplicating its functionality. Is there any reason for not working with an NSSet of Core Data objects provided by the MOC?
If you are working asynchronously, then you should not be sharing an NSManagedObjectContext object across threads, as they are not thread-safe. Instead, create one for each thread, but set them to use same NSPersistentStoreCoordinator. This will serialise their access to the persisted data, but you'll need to use notifications to make them each aware of the others changes.
There is a good tutorial/description on how to use Core Data on multiple threads here:
http://www.duckrowing.com/2010/03/11/using-core-data-on-multiple-threads/
1) CORE DATA AND THREADS, WITHOUT THE HEADACHE
http://www.cimgf.com/2011/05/04/core-data-and-threads-without-the-headache/
2) Concurrency with Core Data
http://developer.apple.com/library/ios/#documentation/cocoa/conceptual/CoreData/Articles/cdConcurrency.html
3) Multi-Context CoreData
http://www.cocoanetics.com/2012/07/multi-context-coredata/

Approaches to save some NSManagedObjects but not others?

I'm working on a Core Data iPhone app that pulls remote resources from the web into NSManagedObjects and saves them locally.
I want the user to be able to designate which of these objects should be saved. This means that some will be saved, but many should be deleted. However, I might want to save and delete at different times - I'd prefer to save designated objects immediately (in case the app crashes), but still keep around the other objects because they're hanging out in table views and such.
One approach I can think of is to have a different persistent store - one for stuff that will be saved, one for stuff that won't; this way I can save the "should be saved" store at any time. However, I would much prefer to keep objects of the same type in the same domain.
Another approach would be to just save at the very end - negating any ability to recover from a crash. But saving at the end would allow me to parse out any objects that weren't designated "should save".
And that's really what I want - a "shouldSave" method in the NSManagedObject class, or at least a save method that I could fire at select objects. But as far as I can tell, neither of those exist.
So, if anyone has any other suggestions, please let me know! It would be greatly appreciated.
CoreData is not for object serialization, it is an object graph serialization. That is an important distinction. Once you have an NSManagedObject it is associated with a context, and CoreData handles saves at context level since that is the only way it guarantee any sort of object graph consistency. In other words, you can't save individual objects because if they have relationships with other objects you would need to also save those objects and it quickly cascades out to the whole graph.
You seem to be worried about crash recovery. If the app crashed and the user relaunched it would they expect to see just the items they saved, or everything that was on the screen before they crashed? If it is the former you should just delete them at save time and remove them from the users view (with some animation), if it is the later you should commit out everything, and potentially delete the objects you are not interested in at another time.