Point of using NSPersistentStoreCoordinator? - ios

In the Core Data lecture from Stanford 193P iPhone course on iTunes, the instructor coded up a sample project with Core Data without using NSPersistentStoreCoordinator and loading it with a NSManagedObjectModel. But in looking at other code samples and the Big Nerd Ranch book on iPhone development, they are creating a NSManagedObjectModel and PersistentStoreCoordinator and setting up the NSManagedObjectContext that way.
My question is what is the purpose of doing it this way, and what are the pros and cons of both approaches?

I followed the same lecture series very closely. This particular example pulls data (Photographers and Photos) from Flickr and loads them into CoreData. It wasn't really necessary to use CoreData in this app since it needs to fetch new data from flickr on every application load, therefore there is no point in saving persistently. The prof was just using the flickr fetching app from the previous demo as a starting point since students were already familiar with it (allowing him to focus on explaining CoreData). However, as rickster mentioned, there are huge benefits to using core data without saving the context to disk.
As Paul explained in the lecture before the demo, a core database can be created (in iOS5) either by:
Clicking "use core data" for an app template when creating a new project.
Using UIManagedDocument
The idea behind the first approach is that Xcode will put a bunch of code in AppDelegate to set up your documents directory/persistent store coordinator/and model. It will then pass the managed object CONTEXT to your initial view controller (which should have an NSManagedObjectContext property in the public API) and from there you can pass the context around like a bottle of beer when you segue to other viewcontrollers. Passing the context around is correct procedure for accessing the core database.
Using UIManagedDocument is very similar, except your AppDelegate is left alone. You create a UIManagedDocument (maybe in your initial view controller) using a URL path from your app's document directory (Note: you have to manually check to see if the file already exits, exists but is not open, or does not exist). Then you can use this document's context in the same way as above.
Another Note: It's a good idea to create a pointer to your context in your AppDelegate so you are able to explicitly save your context (only when it's ready!) when the app crashes or terminates.
The persistent store coordinator is set up automatically for you and you can configure it using it's persistentStoreOptions property (and indeed you will need to in order to save the context persistently), or by subclassing UIManagedDocument and overriding desired methods.
Read the overview in UIManagedDocument documentation
http://developer.apple.com/library/ios/#DOCUMENTATION/UIKit/Reference/UIManagedDocument_Class/Reference/Reference.html
Both methods work the same way and provide you with the same control and access. With UIManagedDocuments you can create multiple databases in multiple sqlite files, you can also wait to create / set up the database until it's needed. The "use core data" option provides you with a single core database which it sets up on application load, allows you to centralize CoreData stuff around AppDelegate, saves coding time and is good for a fast-track app. I like UIManagedDocument.
If you started you app without the core data option checked and would like to add it to AppDelegate, just create a new project with core data checked and copy all the code to your AppDelegate (should just be 3 properties and their accessors as well as a convenience method to access the documents directory). You will need to point in to your initial view controller, model, etc..
UPDATE:
Just wanted to add one other convenience. If your managed object context is stored in your appDelegate, you can access it anywhere in your app just by using
NSManagedObjectContext* context = [[(AppDelegate*) [UIApplication sharedApplication] delegate] myManagedObjectContext];
this negates having to pass it around.
For any CoreData app, if you make any changes to your model, MAKE SURE TO MANUALLY DELETE THE APP IN THE SIMULATOR before building again. Otherwise you will get an error on the next build since it will use the old file.

Without a persistent store coordinator you will be unable to save your results to a persistent area (database, file, etc)...so if you want a persistent data manager that is utterly useless, then omit NSPersistentStoreCoordinator. Are you sure the project wasn't using it? How was the professor saving the data? When you create a new Core Data project, this logic is autogenerated for you.
EDIT: I got it now, the professor is using UIManagedDocument, which uses its own persistent store coordinator internally (based on the file type) so there is no need to create an explicit one (unless you are not satisfied with the default). So, in the end, its not about whether or not to use a coordinator, it is whether or not you explicitly create it.

Related

Passing/Export core data database from one device to another

I'm trying to create an app which will store data locally using coredata. Then I want to share/export that data(from coredata) to be used by other devices using same app.
Is it possible to send a coredata from one device to another? Can I just copy the *.sqlite file and overwrite the coredata in the other device?
I saw this post how to export Core Data to CSV that allow core data to be exported as csv, But what I'm trying to achieve is to pass core data itself.
Thank You!
You can use migratePersistentStore:toURL:options:withType:error: of NSPersistentStoreCoordinator to save the store to a file. Once you have the file you can copy it to the other devices.
Other solutions might include iCloud if you're wanting data synchronisation.
This blog post suggests a good way to export the database as one file. Besides using the migratePersistentStore:toURL:options:withType:error: method, it also creates a copy of the NSPersistentStoreCoordinator to keep the current database consistent, and disables the sqlite write ahead and log to reduce all database to just one file.
In the device receiving the sqlite file, the following worked fine for me:
1 - Save the URL of the persistent store (persistentContainer.persistentStoreDescriptions.first?.url)
2 - Delete the current persistent store, calling persistentStoreCoordinator.remove on your store instance inside persistentStoreCoordinator.persistentStores
3 - Saving the new database in the URL of the old one, using FileManager.default.replaceItem with the URL saved on the first step and the received file.
4 - Call persistentStoreCoordinator.addPersistentStore to add the store to the list of stores that core data will manage.
Also, before executing this whole process it might be good to reset all contexts (context.reset) to ensure no reference was kept, and in my case I executed everything inside the persistentStoreCoordinator.performAndWait method to avoid concurrency problems.

How to use subclass of NSManagedObject?

I'm working on a quite large app on iOS. Up until now, I've been using CoreData like this:
Have a class, that have methods like -(NSArray*)getAllEntries, or -(void)saveEntry:(Entry *)entry, and Entry has a few properties like strings, dictionaries, arrays of other objects that might or might not be saved in CoreData, etc. Mostly, I init Entry with its default ctor, and set values to properties by fetching values from the NSManagedObject by using valueForKey: I get from the CD store.
I started coding like this because at the time I was new to obj-c and I come from a C++/Java background.
Now, I'm working on a new module in the app and want to do everything the obj-c way.
As I understood, if making the Entry object a subclass of NSManagedObject, I could only init it using [NSEntityDescription insertNewObjectForEntityForName:#"Entries" inManagedObjectContext:context];, which means it would be tied to the entity? The thing is, that I might want to for example init that object from the data that I pull from the internet and I might not want to save it to the persistent store. Or, I might want to fetch the object out of the store, edit the values, but not save it to the store. Everything would be fine (probably), but as I understand, if I call the save method on the context which was used to instantiate the object, the object will be saved to the store that I didn't want to be saved.
So now I'm a little confused on how should I continue doing this. Is my old way of doing Core Data Ok, or should I use the subclass of NSManagedObject and use some tricks that I don't know of yet? And if the latter, what are those tricks?
Link answers are discouraged, but you really want to read Apple's extensive documentation on Core Data. Specifically, google for "Creating and Modifying Custom Managed Objects".
(Currently found at this link.)

App structure iOS and Realm: create database when app is installed

I am very new to iOS. I am developing an app with data persistence. I have decided to use Realm for that purpose.
I must to create the database and load data the first time that app runs. I get data from a Web Service in JSON format. I will implement some strategy to update this database later, maybe with iOS Silent Push notifications.
I have read and I have worked about Realm, loading data from JSON... to learn about that.
Now, I need to apply this in my project but I don't know how to start. I need some clues about general idea for the app:
How can I organize my app to load data when it is installed? At what point should I create the database and load data?
I have thought to create a global Realm object y AppDelegate and use it as a global variable. Is it a good idea?
Do I need to set a path for my database? Can I user default path?
If you are looking for a place to start, you can check out the example apps of this UI component add-on for Realm: ABFRealmGridController.
The controller is a subclass of UICollectionView and the example app should demonstrate most of the functionality you are curious about. The example uses the controller to display the top news stories from the New York Times. This involves making a request to their API and loading the JSON response data into Realm.
When to load the data is dependent on how you want the app to function. If the data will be the same for each user, you could bundle the Realm file with the app pre-populated with data.
The ABFRealmGridController example loads data when the user clicks the refresh button and performs the JSON handling on a background thread; a general best-practice.
Finally, unless you have multiple Realms or need to store the file in a specific path, it is probably simplest to use the default path.

How to replace sqlite file with the new one if I use core data?

I am currently working on the app that is going to use database to store items. The first thing that everybody say, when it comes to store data in iOS is Core Data.
But, after few days of looking through tutorials and docs, I have a big question.
Let me explain architecture a little bit more. So we have a backend, where you can add items, also, we have iOS and Android application. I am creating a Core Data model for our database.
What we want,is to check if there is update for database and download it. The problem is that we don't use JSON or XML, we are using the new sqlite file.
Since Core data creates three files for database, which are:
db.sqlite
db.sqlite-wal
db.sqlite-shm
Is core data able to replace "db.sqlite" with the new one, that is downloaded from server?
Thought the idea of replacing the database file instead of importing objects to it is very tempting it's highly discouraged to mess up with sqlite database created by CoreData. You should never touch it manually, when you do, you'll very likely end up with broken DB or messed up data.
So no, CoreDate is not able to replace underlaying sqlite file. You should instead import your data using CoreData stack, that's how it's designed. Creating JSON/XML service is the best way you can go.
BUT IN THEORY and in case you would be able to keep CD internal information stored in the db untouched, it should be possible to replace the sqlite file. If your database is read-only for users, it might work, but if users are able to create or modify records, forget about it right now.
First, you'd have to tear down all CoreData stacks (Managed Object Context, Persistent Store Coordinator, Managed Object Model) that might be using it before you replace it, replace the file and re-create CoreData stack(s).
db.sqlite is the main database file, the other two are temporary files, Write ahead log (wal) and Shared memory (smh), so you don't need these two.
Remember you never MUST NOT change the structure of the database, just data in it!!!
However, as stated in the beginning, I do not recommend this approach at all.

Use of Core Data in iOS

I have been through a couple of these answers here, but I don't think I get it right..
I have several NSArray made of JSON requests. I want to store everything in the app instead of requesting the data all the time, and I understand I should use Core Data for this.
The problem is, I don't know how to initialize this.. I have tried to read up, but I have realized it will take a long time to understand this by just reading the class reference etc.
I have added an .xcdatamodel and created an entity with attributes identical to the data in one of the json object. How can I access the file for extracting and inserting information? I plan on parsing my whole json object into this file, but how can I instantiate the entity? Which delegates and where?
All tutorials I have watched had an option when they created the project, like "Use Core Data" or something, which when checked created lots of code automatically. I don't have that..
You might wanna go through some SO links: here. Also, I once remember going through this guide for adding Core Data to my project. It might help you to go through this link for saving JSON to Core Data directly. I recommended this link here in SO a couple of times. Trust me, all this pain you are enduring setting up Core Data is well worth it when you see things starting to work!
You need to add the core data stack. You can create a new, core data project, and get the core data elements added to the app delegate specifically for core data. You will have 3 properties, and one method. Just copy/paste the declaration and implementation of these elements to your app delegate. Make sure the managedObjectModel method and the persistentStoreCoordinator method are using your actual model name.
To work with core data, you need to read the core data documentation:
http://developer.apple.com/library/mac/#documentation/cocoa/Conceptual/CoreData/cdProgrammingGuide.html
You will have to create entities to represent your data, the properties for the entities, and so fort. Get started with your project, read the documentation, and get started. You will have more questions which you will either ask or find here, but at least you got enough to get started.

Resources