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.)
Related
Thanks to the bellow question's answer, I figured out how to use Cookies between multiple WKWebView by using WKProcessPool.
Cookie sharing between multiple WKWebViews
And then I'm now trying to permanently store the WKProcessPool object (of my singleton object) by using NSUserDefaults.
I've got the following error.
Attempt to insert non-property list object
So I tries to fix the error by converting WKProcessPool object into NSData by NSKeyedArchiver.
But It cannot be done because WKProcessPool does not implement encodeWithCoder: and init:aDecoder.
How can I solve this problem to store WKProcessPool object permanently?
You need to implement NSCoding for your custom objects. Here is Ray Wenderlich's tutorial
Edit:
I really didn't have idea that WKProcessPool is in built class, since apple doesn't implemented the NSCoding in it and I can't even see the public properties of this class, so even if you subclass it, you don't have any idea which properties to encode/decode.
You need to find out other solution instead of archiving/archiving the objects. Even if you achieve something using category I'm afraid that you will get the expected result.
I need a suggestios on how to correctly implement CoreData with objects that can be uploaded/downloaded from server and sent through game center.
The app is similar to a trading card game, you can get the idea from these 2 separate entities:
Card: The actual unique cards with all the information about each one. The "Card"s entities are static and do not change, they do not need to be sent on server either because all I need to do is send the "CardId" to pull a "Card" entity.
UserCard: All the cards that a user owns, a user may have the same card multiple times but this would be a different "UserCard" with a pointer ("CardId") to a basic "Card". They are always changing. They need to be easily sent through GameCenter and uploaded/downloaded from server. Also, some temporary "UserCard"s would need to be downloaded from server when the user visits a friend's profile to see which cards they have.
Does anyone have any suggestion on how to do this correctly? Currently I have two ideas:
A. Use CoreData for everything, this would mean that the "UserCard"s are NSManagedObject subclasses which get intelligently encoded/decoded (using NSCoding) to upload/download and send through GameCenter. The basic "Card" is set as a relationship in Core Data. A "temporary" attribute is also set for the "UserCard"s to be able to know which ones do not belong to the current user so they can be deleted later.
B. Use CoreData only for the Basic "Card"s and use a NSObject subclass for the "UserCard"s. This allows me to directly use the "UserCard"s without the need to insert them into CoreData. Makes it easier to download/upload and send through GameCenter. It also removes the need for the "temporary" property because the objects will just be deallocated when the view has stopped using them. The problem with this method is that I would need another way to store the current user's "UserCard"s in the device because they do need to be available offline.
Thank you!
Personally, I would use CoreData all around. Reasons:
You're already having to dive into CoreData to deal with Cards anyways
Just create an NSManagedObject category called NSManagedObject+JSON. In it have a simple method called -(NSDictionary*)jsonRepresentation and have the UserCard format itself into a dictionary and return itself. Super easy.
It would be safer to use CoreData and save often, rather than run the risk of writing to a file and it either: 1. getting corrupted (highly unlikely but user would be mad if it did); or 2. not getting fully written (user could kill the app before it has time to write everything to a file)
The extra little bit of work to implement the UserCard entity would be small.
You get the built memory management and searching functions of CoreData.
I just finished a large project working with CoreData so I may be a little biased. From what you've stated in your question though, I would take CoreData for everything. Here are some helpful opensource libraries when dealing with CoreData and a web server:
MagicalRecord
AFIncrementalStore
And a great tutorial for integrating CoreData with a web server.
i want to store some managed objects as to make searching later on easier, what is the best way to do that? (So that it survives exiting the app)
I am thinking user defaults (but im not sure if you can store managed objects like this)
Or some sort of cache?
SO, although I am not sure this is the best way to do it, here is how I fixed this issue. I was looking for a way to preload a list of results that would persist through launches.
To accomplish this I created an NSMangedObject, called it HistoryObject, and added a relationship (to-many) to the object I wanted to store. I added those objects to this object (basically an NSManagedObject array of objects), and retrieve this object whenever I want to load the subObjects...
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.
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.