JSON to Persistent Data Store (CoreData, etc.) - iphone

All of the data on my application is pulled through an API via JSON. The nature of a good percentage of this data is that it doesn't change very often. So to go and make JSON requests to get a list of data that doesn't change much didn't seem all that appealing.
I'm looking for the most sensible option to have this JSON saved onto the iPhone in some sort of persistent data store. Obviously one plus of persisting the data would be to provide it when the phone can't access the API.
I've looked at a few examples of having JSON and CoreData interact, for example, but it seems that they only describe transforming NSManagedObjects into JSON. If I can transform JSON into CoreData, my only problem would be being able to change that data when the data from the API does change.
(Or, maybe this is all just silly.)

For some conceptual reading, you may want to read documentation about Efficiently Importing Data, especially "Find-or-create". See a previous similar question.
Getting JSON and saving it as Core Data locally is highly reasonable. How I do it is in two stages:
convert JSON to native Cocoa data types (NSDictionary and NSArray)
convert NS* into Core Data object
Two good frameworks for converting between JSON and NS* are json-framework and TouchJSON. The below example is based on json-framework.
Let's say you get an array of some objects from JSON. You'd then do:
NSArray *objects = [jsonStringYouGotFromServer JSONValue];
for (NSDictionary *_object in objects) {
CDObjectType *cdObject = [self cdObjectFromDictionary:_object];
// cdObject is now a full-featured Core Data object
}
the cdObjectFromDictionary might be something like this:
- (CDObjectType *) cdObjectFromDictionary:(NSDictionary *)dict {
CDObjectType *object = [NSEntityDescription
insertNewObjectForEntityForName:#"Object"
inManagedObjectContext:moc];
NSDictionary *attributes = [[NSEntityDescription
entityForName:#"Object"
inManagedObjectContext:moc] attributesByName];
for (NSString *attr in attributes) {
[object setValue:[dict valueForKey:attr] forKey:attr];
}
return object;
}
The above assumes that the attribute names in your JSON and Core Data model are the same and that the data types match (i.e JSON numbers are Core Data NSNumbers etc). The above code works nicely even if the model changes.
The above code does not consider how to test if the object already exists locally, but you can imagine how to add that. You must have an object ID of some form in your model, and you can see if the object exists locally before adding it, or whether existing object needs to be updated.

Accessors may be needed when there are to-many relations.
It is more efficient to use accessors than to use key-value. (Internally Core Data may use key-value anyway, so this point may not be true if that is the case.)
Using subclass and properties, the compiler can help you to detect problems, while using key-value, the problems will only show at runtime and harder to fix.
Therefore, it is also good to subclass NSManagedObject and use generated properties and accessors.

Related

Transformable Collection in Core Data with custom Objects

I have to store a collection of custom objects (Dictonary) in Core Data Database.
So far so good. The Dictonary is stored and can be loaded without problems as a "Transformable" object.
The custom Object holds properties, but these are nil after loading them from the Database.
After searching a lot, I haven't found anything for this problem.
It seems that the properties are not getting stored in this way. (Maybe because only the address is stored and not the data??)
Sure it would be better to store an object of Core Data supported datatypes, but in this case the transformable Object is just fine and saves me a lot work and time.
Thank U!
The whole idea of transformable objects is covered in the Core Data Guide. Note that this uses a keyed archiver / unarchiver to create a NSData object from your object or the reverse. This means your customer objects my adhere to NSCoding, and encode all the information in them when asked to as well as handle unencoding.
If your are not doing this now this is the root cause of your problem. What I suggest you do is adopt NSCoding in one custom object, then verify that in fact you can encode it to a NSData object, then from the object unencode it and get the same object back. When you have that working you can then test with Core Data.

is it worth to store JSON into core data objects?

I download a bunch of JSON objects in my app, and save the file as an nsdata object:
[responseData writeToFile:appFile atomically:YES];
Then whenever I want some of the json object, I load that data, serialize, and filter.
By now, it works great.
And, I'm gonna start downloading new types of JSON objects (with different properties), from the web.
¿Should I start using core data, or continue this way (meaning, no problems doing this)?
Depends on the scale of objects you'll be writing and serializing. If there can by a dynamic amount of these objects..thus potentially being alot, you should probably Look more into Core Data as the option for Storage.

Using NSManagedObject

I'm learning Core Data and trying to parse XML from some web service and save it into data storage.
Is it the best practice to save data from XML directly into managed objects? Or there are some better ways to do this operation?
Thank you!
You should be convert your data into something that is useful for the application and not just store the raw tagged data when parsing the XML. That way you only do the conversion once in the XML Parser and not every time you want to use the value elsewhere in the code.
For example, an integer stored in an XML file is always going to appear as a string at first, so using [NSNumber numberWithInt:[string intValue]] in the XML parser once is far better than having that extra bit of code peppered throughout your application.
I think you didnt understand what i mean. If i want to parse some musical album info from XML and save it into Data Storage should make something like Album* album = [NSEntityDescription insertNewObjectForEntityForName:#"Album" inManagedObjectContext:context]; to create object where i will save data from XML or i have to create some another Album class not inherited from NSManagedObject to work with it while parsing
I don't think there's a direct way do convert an XML document into a managed object. I use NSXMLDocument to do exactly what you want to do but you will have to do a little parsing.
I would create your objects directly from your parsed XML. In other words, just use your NSManagedObject derived classes. No intermediate class hierarchy is necessary. Also, if you're not already, you should use mogenerator + xmod for re-generating your data object classes automatically whenever your model changes.

How to efficiently get all valid values of an attribute from an NSManagedObject?

I have an iPhone app with a Core Data object that has a "color" attribute. I'd like to get a list of all the values for color that have been saved. A simple SQL statement SELECT DISTINCT(color) FROM myObjectTable would easily do this. How can I do this in Core Data without loading all the objects (of which there may be thousands) into an in-memory NSSet?
You can:
1) set NSFetchRequest's requestType to NSDictionaryResultType
2) "setPropertiesToFetch" in NSFetchRequest to fetch only the property instead of the whole object.
I haven't found a good solution to this yet either. But you can as Nevin suggests get specific attributes instead of the entire managed objects.
See Fetching Specific Values from Apple's documentation for more detail.
You will get a NSArray of NSDictionary objects that you can then loop through, extracting the color values that you are looking for.

Custom UID data type for CoreData

The CoreData documentation says "You can sometimes benefit from creating your own unique ID (UUID) property which can be defined and set for newly inserted objects. This allows you to efficiently locate specific objects using predicates (though before a save operation new objects can be found only in their original context)."
What should be used for this type?
A managed object's objectID is usually 'NSManagedObjectID' type but the DataModel wizard tool via XCode that allows you to set the type for a given attribute only has the basic allowed types in addition to 'Undefined', Binary Data, & Transformable.
If I wanted to have an attribute that serves as a secondary id for an object (in addition to it's standard ObjectID), do you store it as an NSString or would you custom modify the object model to hold NSManagedObjectID?
(for iPhone app/CoreData development)
An NSString or integer attribute are logical choices, though you could use a transformable attribute to store anything you wanted (that could be appropriately serialized, of course). An incrementing integer is probably good enough for many uses, but each use case is different. Many algorithms exist on the net for generating string or byte-array UUIDs (start with Google). An NSString UUID is quite easy:
+(NSString*)UUIDString {
CFUUIDRef theUUID = CFUUIDCreate(NULL);
CFStringRef string = CFUUIDCreateString(NULL, theUUID);
CFRelease(theUUID);
return [NSMakeCollectable(string) autorelease];
}
for an array of bytes, look at CFUUIDGetUUIDBytes().
Before you go this route, think long and hard about whether it is necessary. Folks coming from a SQL point of view "want" their ids, but Core Data is not about relational databases. It's an object graph management framework that just happens to use SQLite as one backing implementation. If you're trying to do SQL-like things in Core Data, you're going to be fighting the framework. There's often a way around needing a separate id property in proper usage of the Core Data framework.