insert NSDictionary into CoreData - iphone

I have an NSDictionary and a CoreData Database. I want to insert the NSDictionary into the database.
How can I do this (code snippets if possible)?
What is the suitable type for the attribute of the dictionary ?

You would need to serialize your NSDictionary to an NSData, CoreData can hold to NSData.
But you won't be able to search (predicate) element of your NSDictionary.
And if we think about this, NSDictionary is a collection of data.
A Table in a database is kind of a collection of data.
In CoreData the closest thing you got to a collection of data is NSManagedObject.
So my advice would be to make a NSManagedObject subclass that would hold the information you have in your NSDictionary. The key would be the attribute and the value would be the value of that attribute. And you would be able to search based on the attribute of that NSManagedObject subclass.

I found another way to add Dictionary into Coredata by creating an attribute with data type 'Transformable'.
For example create an entity in your project & attribute with data type Transformable.
Generate subclass for NSManagedObject. Attribute will be available with data type 'id', change into NSDictionary.
Below is what I did (My NSManagedObject sub class name is 'DictTest')
-(void)InsertIntoDataBase
{
DictTest *entityDict=(DictTest*)[NSEntityDescription insertNewObjectForEntityForName:#"DictTest" inManagedObjectContext:self.managedObjectContext];
NSMutableDictionary *mutDict=[NSMutableDictionary dictionary];
[mutDict setValue:#"1" forKey:#"1"];
[mutDict setValue:#"2" forKey:#"2"];
[mutDict setValue:#"3" forKey:#"3"];
[mutDict setValue:#"4" forKey:#"4"];
[mutDict setValue:#"5" forKey:#"5"];
[mutDict setValue:#"6" forKey:#"6"];
[mutDict setValue:#"7" forKey:#"7"];
[mutDict setValue:#"8" forKey:#"8"];
[mutDict setValue:#"9" forKey:#"9"];
[mutDict setValue:#"10" forKey:#"10"];
[entityDict setInfoDict:mutDict];
NSError *error;
if(![self.managedObjectContext save:&error])
{
NSLog(#"error description is : %#",error.localizedDescription);
}
else{
NSLog(#"Saved");
}
}
for Fetching records
-(void)FetchRecord
{
NSFetchRequest *request=[[NSFetchRequest alloc]init];
NSEntityDescription *entity=[NSEntityDescription entityForName:#"DictTest" inManagedObjectContext:self.managedObjectContext];
[request setEntity:entity];
NSArray *fetchArray= [self.managedObjectContext executeFetchRequest:request error:nil];
for (DictTest *obj in fetchArray) {
NSLog(#"Dict is : %#",obj.infoDict);
}
}

Set up your entity description, insert a new object, then use:
[managedObject setValuesForKeysWithDictionary:dict];

Why don't you just create an entity with all the data from your NSDictionary and then just parse through it.
Check this out for some CoreData code snippets. You can simply create a few entities to store the info of your dictionaries. You can then parse through your dictionary and save the proper attributes:
NSManagedObject *photoObject = [NSEntityDescription insertNewObjectForEntityForName:#"Photo"
inManagedObjectContext:context];
[photoObject setPhotographer:[myDictionary objectForKey:#"photographer"]];
and so on...
No matter how complex you XML data structure, if you can set up a nice entity structure, it is fairly easy to simply dumb it all in CoreData. You'll also have a much easier time with queries if you take the time to create entities instead of just dumping the whole dictionary in a single field.

Related

Need to update nsmanagedobject from nsarray for loop - iphone

I have a coredata project that I'm trying to programmatically update a number.
I'm retrieving objects from CoreData and then storing it into an array.
Then, I'm looping through that array to see if the current user's IP is present in the database and trying to update the number of times accessed for that specific array.
The problem is, it's updating all the objects, not just the current object in the looped array.
First, I get the info from core data like so:
- (void)fetchRecords {
// Define our table/entity to use
NSEntityDescription *entity = [NSEntityDescription entityForName:#"IPAddr" inManagedObjectContext:managedObjectContext];
// Setup the fetch request
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:entity];
// Define how we will sort the records
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"ipDate" ascending:NO];
NSArray *sortDescriptors = [NSArray arrayWithObject:sortDescriptor];
[request setSortDescriptors:sortDescriptors];
// Fetch the records and handle an error
NSError *error;
NSMutableArray *mutableFetchResults = [[managedObjectContext executeFetchRequest:request error:&error] mutableCopy];
if (!mutableFetchResults) {
// Handle the error.
// This is a serious error and should advise the user to restart the application
}
// Save our fetched data to an array
[self setIpArray: mutableFetchResults];
}
Now, I'm trying to find if the current User IP is present in the fetched results, and if it's present, update the number of times accessed:
// see if the ip is present and update if necessary
-(void)ipPresent {
NSString * theCurrentIP = [self getGlobalIPAddress];
for (IPAddr *allips in ipArray)
{
if ([allips.ipNum isEqualToString:theCurrentIP]) {
NSLog(#"The IP %# was found.", theCurrentIP);
// update the ip
NSError *error = nil;
NSNumber *ipToUpdate = allips.ipAccess;
NSNumber *addIpAccess = [[NSNumber alloc] initWithInt:1];
NSNumber *updateIpAddress = [NSNumber numberWithFloat:([ipToUpdate floatValue] + [addIpAccess floatValue])];
[self.ipArray setValue:updateIpAddress forKey:#"ipAccess"];
if ([self.managedObjectContext save:&error]) { // write to database
NSLog(#"The IP Was Updated from %# to %#", ipToUpdate, updateIpAddress);
} else if (![self.managedObjectContext save:&error]) {
NSLog(#"failed with error: %#", error);
}
break;
} else {
NSLog(#"The IP %# was NOT found.", theCurrentIP);
}
}
}
I'm pretty sure the issue is with this line:
[self.ipArray setValue:updateIpAddress forKey:#"ipAccess"];
Again, it's updating ALL the entities and not just the one in the current loop.
Indeed. You are using the wrong method. self.ipArray is a NSMutableArray.
The method
- (void)setValue:(id)value forKey:(NSString *)key
is used for Key-Value Coding (which is what makes it work for Core Data objects), but when applied to an array, it will invoke setValue:forKey: on each entry in the array.
Now, you can see that you could also call setValue:forKey on the one single array element allips since its property is obviously KVC compliant -- otherwise you would be having a different problem, not see the values being set.
Note, that you could also just assign the property...
allips.ipAccess = updateIpAddress;
EDIT
Sorry, probably should have read slower... You do understand that you don't have to use a mutable array, right? You are not actually changing the array, just the elements in the array. An immutable collection means that the collection contents can not change, but when you have a pointer to an object, as long as that object is not immutable, you can still mutate its properties.
Thus, if you had an immutable array of Foo objects, you could do this...
for (Foo *foo in myImmutableArray) {
Bar *bar = [self getSomeNewBar];
[foo setBar:bar];
// If Foo is KVC compliant, you can do this too...
[foo setValue:bar for Key:#"bar"];
}
If, however, you call setValue:forKey on the array, it will be invoked for each element of the array. Note, that setValue:forKey is actually declared in the immutable NSArray.
EDIT
That comment was hard to read.
The core data object is just another object. It looks like you have subclassed it, and provided it with properties for the attributes. Just replace
[self.ipArray setValue:updateIpAddress forKey:#"ipAccess"];
with
[allips setValue:updateIpAddress forKey:#"ipAccess"];
or
allips.ipAccess = updateIpAddress;
Either of those should modify your core data object, as they would any object that had a read/write property named "ipAccess"
Assuming, of course, that I didn't read it wrong again... and allips is your core data object...

How to insert NS Mutable Array into Core Data

-(void)dataManagerDidFinishLoading:(DataManager *)datamgr
{
NSLog(#"%#.....",datamgr);
pageArray=[datamgr.resultDataDictionary objectForKey:#"POS_GetPageResult"];
GetPage *page=(GetPage *)[NSEntityDescription
insertNewObjectForEntityForName:#"GetPage"
inManagedObjectContext:managedObjectContext];
NSError *error;
if (![managedObjectContext save:&error]) {
// This is a serious error saying the record could not be saved.
// Advise the user to restart the application
NSLog(#"Error........");
}
[pageArray insertObject:page atIndex:0];
}
Use NSKeyedArchiver:
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:array];
Note that all the objects in array must conform to the NSCoding protocol. If these are custom objects, then that means you need to read up on Encoding and Decoding Objects. You can store the NSData as BLOB in the database. Hope this helps you.
You can use the transformable core data type. Then your object will be parsed to an id. Just keep in mind that the transformable attribute will be passed to a NSData object (and reversed) to be stored (or retrieved) in core data.

Does coredata save after a change like this?

I have CoreData in my app, with an Entry class, which contains an NSOrderedSet of Media classes.
I then have this code, for adding a new Media item to the NSOrderedSet:
-(void)addImage:(UIImage *)image isInPhotoLibrary:(BOOL)isInPhotoLibrary {
Media *media = [[Media alloc] init];
media.type = #"Image";
media.originalImage = UIImageJPEGRepresentation(image, 1.0);
media.isInPhotoLibrary = [NSNumber numberWithBool:isInPhotoLibrary];
[self addMediaObject:media];
}
Will this automatically save the changes, or will I have to do it myself. If so, will i then need to pass in a context to do this, or is there another way?
No, this code doesn't have any Core Data references at all.
Is Media an NSManagedObject? If so you need to be creating it like so:
Media *media = [NSEntityDescription insertNewObjectForEntityForName:#"Media" inManagedObjectContext:context];
This will put it in your managed object context.
If you then want to persist it, you will need to call save: on the managed object context.
EDIT ALSO....
In your Entry class, you will probably have a generated method that you use to add objects to the NSSet. It will be in a category (CoreDataGeneratedAccessors) on the Entry header file
- (void)addMediaObject:(Media *)value;
No it won't.. If you want to save changes to Database in Core data you gotta call save function for that.. I assume Media is kind of NSManagedObject class. To save the changes to persistent store you have to call save method . Until then the changes are just temporary present on your scratch board/ ManagedObjectContext.
This is how I save changes:
Worker *worker = (Worker *)[NSEntityDescription insertNewObjectForEntityForName:#"Worker" inManagedObjectContext:self.managedObjectContext];
worker.name=txtContact.text;
worker.address=txtAddress.text;
worker.zipCode=txtZip.text;
worker.city=txtCity.text;
worker.mobile=txtMobile.text;
NSError *error;
if (![managedObjectContext save:&error])
{
NSLog(#"Whoops, couldn't save: %#", [error localizedDescription]);
}

Core Data: Save unique object ID

I see that there is a method of getting the unique id of a managed object:
NSManagedObjectID *moID = [managedObject objectID];
But, as I have read, that changes when it is saved.
What is a better way of creating my own unique ID and saving it in each object, making sure that it IS unique and does't change?
You cannot save the NSManagedObjectID in a CoreData entity, and there's no properties in it that can be intended as integer or string ID.
Therefore building your own unique identifier algorithm is an acceptable solution, if it's impossible for you to keep track when the entity is saved, I did this for some applications.
For example I had a similiar problem time ago, when I needed to fill a UITableView cells with a reference to the entity, and retrieve the entity after clicking/touching the cell.
I found a suggestion by using the uri representation, but however I still needed to save the context before, but I manage to use the NSFetchedResultsController and found a more solid solution rather than an application built id.
[[myManagedObject objectID] URIRepresentation];
Then you can later retrieve the managed object id itself:
NSManagedObjectID* moid = [storeCoordinator managedObjectIDForURIRepresentation:[[myManagedObject objectID] URIRepresentation]];
And with the moid I could retrieve your managed object.
I have a created date property in my object, so I just ended up using that date including seconds, which, seems to me like it will be unique and work how I want.
You can create an id field for your object, and populate it during init using GUID. for how to create a GUID and optionally export it to string see UUID (GUID) Support in Cocoa
If it helps anyone else searching for this, this is how I do it:
Create an ID property on the object
Get the last used ID when creating an object with this code:
Playlist *latest;
// Define entity to use and set up fetch request
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Playlist" inManagedObjectContext:managedObjectContext];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:entity];
// Define sorting
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"listID" ascending:NO];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
[request setSortDescriptors:sortDescriptors];
// Fetch records and handle errors
NSError *error;
NSArray *fetchResults = [managedObjectContext executeFetchRequest:request error:&error];
if (!fetchResults) {
NSLog(#"ERROR IN FETCHING");
}
if ([fetchResults count] == 0) {
latestID = 0;
} else {
latest = [fetchResults objectAtIndex:0];
latestID = latest.listID;
}
Increment this by one on the new object.

How to update an item in core data

I am using Core Data. I retrieve data in a NSMutable array using some NSPredicate. Now I want to update some items. Let say I have a name string or some BOOL which I want to update. So how to do that. Like Is there any way I can update with respect to ID or something, becoz I dont get all the items from the managedObjectContext.
You assign the data to the objects. and then call
[managedObjectContext save:&error];
on each the respective context object. You can call
[managedObject managedObjectContext];
to get that items context
To get a managed object with a certain id you use
NSFetchRequest and it's setPredicate method. then something like this
[request setEntity:[NSEntityDescription entityForName:#"Shirt" inManagedObjectContext:moc]];
NSArray *shirtsInDB = [moc executeFetchRequest:request error:nil];
or this method in NSManagedObjectContext
- (NSManagedObject *)objectWithID:(NSManagedObjectID *)objectID