How to update transformable attribute in core data - ios

I am in a situation where i need to update transformable attribute in my entity in core data, until now i've tried every possible answer from google and stack overflow but did't achieve anything.
This is the method where i am saving object in core data, and my object which i am saving is an NSMutablDictionary type object.
-(void)didSaveToCoreData :(NSMutableDictionary *)newDict
{
#try {
AppDelegate *appDelegate = [[UIApplication sharedApplication]delegate];
NSManagedObjectContext *context = appDelegate.managedObjectContext ;
DataModelSupport *entity = [NSEntityDescription insertNewObjectForEntityForName:#"CPIEntity" inManagedObjectContext:context];
if (newDict != nil) {
[entity.fixed_Model removeAllObjects];
entity.fixed_Model = newDict;
}
NSError *error ;
[context save:&error];
if(error)
{
NSLog(#"Error in Saving Data");
}
else
{
[self didFetchFromCoreDataModel];
NSLog(#"Successfully saved");
}
}
#catch (NSException *exception) {
[self spareMeFromTheCrash:exception];
}
#finally {
}
}
in this method i am saving a dictionary object of 19 key/value, at the first time and i am fetching it correctly in didFetchFromCoreDataModel method, but when i refresh the data and get dictionary of 18 key/value i save that dictionary in core data using the same method didSaveToCoreData and fetch it in the same way from didFetchFromCoreDataModel but it still show 19 key/value
DataModelSupport is a subclass of NSManagedObject.
In DataModelSupport.h:
#property (nonatomic,weak) NSMutableDictionary *fixed_Model;
In DataModelSupport.m:
#dynamic fixed_Model;
This is it for the DataModelSupport class.
Now here in this method i am fetching the same object form core data
-(void)didFetchFromCoreDataModel
{
#try {
AppDelegate *appDelegate = [[UIApplication sharedApplication]delegate];
NSManagedObjectContext *context = appDelegate.managedObjectContext ;
NSEntityDescription *entity = [NSEntityDescription entityForName:#"CPIEntity" inManagedObjectContext:context];
NSFetchRequest *request = [[NSFetchRequest alloc]init];
[request setReturnsDistinctResults:YES];
[request setReturnsObjectsAsFaults:NO];
[request setResultType:NSDictionaryResultType];
[request setEntity:entity];
NSError *error ;
NSArray *arr = [context executeFetchRequest:request error:&error];
updatedfinalArr = [arr valueForKey:#"fixed_Model"];
if(error)
{
NSLog(#"Error");
}
else
{
}
}
#catch (NSException *exception) {
[self spareMeFromTheCrash:exception];
}
#finally {
}
}
And this is how my core data looks like:-
Any help is appreciated.
EDIT
I've implemented some changes in my code now in didSaveToCoreData method i am using this line of code to fetch the Entity by name
NSEntityDescription *descriptor = [NSEntityDescription entityForName:#"CPIEntity" inManagedObjectContext:context];
by this i am not creating new entity every time i call didSaveToCoreData method.
and this is how i am saving NSMutlableDictionary object
DataModelSupport *entity = [[DataModelSupport alloc]initWithEntity:descriptor insertIntoManagedObjectContext:context];
[entity.fixed_Model removeAllObjects]
entity.fixed_Model = newDict;
but still i am not getting correct result.
now when i refresh the data and save it using the above procedure explained in EDIT section, and fetch it, i get the updated data but it increase the number of objects, like on first attempt when i fetch i got 1 object in array, and on second attempt i got 2 objects and it goes like this, so when ever new data is added its not updating it but instead it add it in the entity s fixed_Model attribute and increase the number of object.
Lastly now i am using this line of code to get the last and update object from array in didFetchFromCoreDataModel method
NSDictionary *ddd = [[arr valueForKey:#"fixed_Model"]lastObject];
updatedfinalArr = [NSMutableArray arrayWithObject:ddd];

Your save method creates a new CPIEntity object each time. So, unless you delete the old object elsewhere in your code, I suspect your fetch is returning several objects, the first of which has the dictionary with 19 key/value pairs in the fixed_Model attribute, and the second/subsequent objects contain the 18 key/value pairs.
When you save, you should try to fetch the existing object first, and if you get zero results then create a new object. Then set the fixed_Model attribute of the new/existing object to your new dictionary.
EDIT
You are still inserting a new object each time (DataModelSupport *entity = [[DataModelSupport alloc]initWithEntity:descriptor insertIntoManagedObjectContext:context];). See below for an example of "fetch or create":
AppDelegate *appDelegate = [[UIApplication sharedApplication]delegate];
NSManagedObjectContext *context = appDelegate.managedObjectContext ;
NSEntityDescription *descriptor = [NSEntityDescription entityForName:#"CPIEntity" inManagedObjectContext:context];
NSFetchRequest *request = [[NSFetchRequest alloc]init];
request.entity = descriptor;
NSError *error;
NSArray *results = [context executeFetchRequest:request error:&error];
if (results == nil) {
// This implies an error has occurred.
NSLog(#"Error from Core Data: %#", error);
} else {
if (results.count == 0) {
// No objects saved, create a new one...
DataModelSupport *entity = [[DataModelSupport alloc]initWithEntity:descriptor insertIntoManagedObjectContext:context];
entity.fixed_Model = newDict;
} else {
// At least one object saved. There should be only one
// so use the first...
DataModelSupport *entity = [results firstObject];
entity.fixed_Model = newDict;
}
}
I've assumed for simplicity that newDict is not nil; amend as appropriate to handle that case.

Can you narrow down the problem?
Ie. can you compare the two Dictionaries..the original one with 19 values and the new one with 18 values?
Is there a particular entry which is not being 'removed'? That might point to a challenge with 'delete' (or the lack there of).
Alternatively, if you completely replace the content, what result do you get on fetch?

Related

Not saving inserted core data in objective c

I'm trying to insert a new object to my core data.
I inserted this way in previous app and it was ok.
Here is the function
_appDelegte=[UIApplication sharedApplication].delegate;
self.managedObjectContext = _appDelegte.managedObjectContext;
#try {
Album *album=[NSEntityDescription insertNewObjectForEntityForName:[AlbumTable tableName] inManagedObjectContext:self.managedObjectContext];
//Insert Values
album.albumId=albumId;
album.code=code;
album.name=name;
album.facebookAccessToken=facebookAccessToken;
album.facebookExpires=facebookExpires;
album.albumDescription=description;
album.startTime=startTime;
album.endTime=endTime;
album.latitude=latitude;
album.longitude=longitude;
album.placeName=placeName;
album.ownerFacebookId=ownerFacebookId;
album.isWatermark=isWatermark;
return YES;
}
#catch (NSException *exception) {
NSLog(#"Insert exception - %#", exception.description);
return NO;
}
Later in the app' i'm trying to get the object out and it's nil.
// initializing NSFetchRequest
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
//Setting Entity to be Queried
NSEntityDescription *entity = [NSEntityDescription entityForName:[AlbumTable tableName] inManagedObjectContext:_appDelegte.managedObjectContext];
[fetchRequest setEntity:entity];
NSError* error;
// Query on managedObjectContext With Generated fetchRequest
NSArray *fetchedRecords = [_appDelegte.managedObjectContext executeFetchRequest:fetchRequest error:&error];
// Returning albums as an array
for (Album *album in fetchedRecords) {
return album;
}
return nil;
Any idea why?
Check if
Context is not nil
You call save: on the context
Other conspicuous points: the following line of code.
Album *album=[NSEntityDescription insertNewObjectForEntityForName:[AlbumTable tableName] inManagedObjectContext:self.managedObjectContext];
You have a class album table that returns the table name. I would not advise to use something this complicated. Simply use a string #"Album".
and this
// Returning albums as an array
for (Album *album in fetchedRecords) {
return album;
}
This is not returning the albums as an array. It just returns the first album of the array. Make sure that also the fetch situation has a non-nilmanaged object context.

Can't overwrite Core data iOS/objective C

so I'm trying to overwrite/update a value saved from core data. when the back button is pressed (gets the textfield data and then overwrites the data using that). But it just keeps adding new data in. Here's my code in the back button:
The IF statement is just checking what the index is so it knows which view controller to go back to. goBackMVC just takes it back to a certain view controller.
- (IBAction)btnBack:(UIBarButtonItem *)sender {
if (self.viewControllerIndex == 3) {
NSLog(#"test");
[self saveDataMethod];
[self goBackMVC];
[self.navigationController popViewControllerAnimated:YES];
}
saveDataMethod:
- (void) saveDataMethod {
NSManagedObjectContext *context = [self managedObjectContext];
// Create a new managed object
FavouriteItem *favouriteItem = [NSEntityDescription insertNewObjectForEntityForName:#"FavouriteEntity" inManagedObjectContext:context];
favouriteItem.webName = self.txtName.text;
favouriteItem.webURL = self.txtURL.text;
favouriteItem.imageURL = self.txtImageURL.text;
NSLog(#"favouriteItem.webName %#", favouriteItem.webName);
NSError *error = nil;
// Save the object to persistent store
if (![context save:&error]) {
NSLog(#"Can't Save! %# %#", error, [error localizedDescription]);
}
}
My question is how can I overwrite the data instead of just adding it? Thanks.
edit: I've searched around and a lot of solutions have arrays, but I'm not allowed to use arrays
This is because you insert a new entity to your core data :
FavouriteItem *favouriteItem = [NSEntityDescription insertNewObjectForEntityForName:#"FavouriteEntity" inManagedObjectContext:context];
Instead fetch the required entity :
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
[fetchRequest setEntity:[NSEntityDescription entityForName:#"Favorits" inManagedObjectContext:context]];
To get the required entity create an NSPredicate instance to filter the required entity (in case you have more than one) and use it in your request :
NSPredicate *filterPredicate = [NSPredicate predicateWithFormat:<Your filter string>];
[fetchRequest setPredicate:filterPredicate];
NSError *error = nil;
NSArray* entities = [context executeFetchRequest:fetchRequest error:&error];
if ([entities count] == 1) {
// Get the entity and update necessary fields and save in context
}

Having only one instance of an entity in Core Data

I am making an application where you need to log in with a 4 digit password but there can only be one password at a time. I am trying to save it to core data but whenever the user adds a new password it just adds it to the long list. How can I restrict an entity to only have one instance of itself?
Here is my code just in case it will help:
-(BOOL)savePassword:(NSString*)password{
AppDelegate * appDelegate = [[AppDelegate alloc]init];
NSManagedObjectContext * context = [appDelegate managedObjectContext];
AppData * appData = (AppData*)[NSEntityDescription insertNewObjectForEntityForName:#"AppData" inManagedObjectContext:context];
appData.password = password;
NSError *error;
if (![context save:&error]) {
NSLog(#"Whoops, couldn't save: %#", [error localizedDescription]);
}
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"AppData" inManagedObjectContext:context];
[fetchRequest setEntity:entity];
NSArray *fetchedObjects = [context executeFetchRequest:fetchRequest error:&error];
if (fetchedObjects == nil) {
NSLog(#"There was an error:%#",error);
}
for (AppData * adata in fetchedObjects) {
NSLog(#"Password:%#",adata.password);
}
return YES;
}
Thanks!
The right approach here is to not put this data in Core Data. If you only have one instance, there's no point in using Core Data to solve the problem. There's no benefit to using Core Data for this. Put it somewhere else. Code solutions miss the point, because even if it works, it's a bad design.
You should do like this, first create fetch request and execute a fetch. check if object exist, update data. else if no data exist create an object and save it.
If name of entity which is storing password.
Your code should look like this
AppData * appData;
NSManagedObjectContext * context = [appDelegate managedObjectContext];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"AppData" inManagedObjectContext:context];
[fetchRequest setEntity:entity];
NSArray *fetchedObjects = [context executeFetchRequest:fetchRequest error:&error];
if(fetchObjects.count > 0){
appData = [fetchObjects objectAtIndex:0];//assume there will be one object
// and do reset of thing
}
else{
appData = (AppData*)[NSEntityDescription insertNewObjectForEntityForName:#"AppData" inManagedObjectContext:context];
}
appData.password = password;
// save moc here
[context save:nil];

Unable to replace one Entity with another in Core Data in iOS

I am working on an iOS application where I am using Core Data for storage. In my store, every entity will be unique, and I'm building a function where I replace one existing entity with another that I pass in. Here is an example of an entity that I pass:
NSManagedObjectContext *context = [[MyDB sharedInstance] managedObjectContext];
User *user = [NSEntityDescription insertNewObjectForEntityForName:#"User" inManagedObjectContext:context];
NSNumber *userNumber = 12345;
user.id = userNumber;
user.name = #"John Doe";
user.email = #"john#doe.net";
user.createdDate = [NSDate date];
[[MyDB sharedInstance] updateUser:user];
Inside my Core Data storage, I have an identical Entity already, except that the email address is "john#doe.com". My update at the moment looks like this:
-(void)updateUser:(User *)user {
NSError *error;
NSManagedObjectContext *context = [[MyDB sharedInstance] managedObjectContext];
// Create fetch request
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"User" inManagedObjectContext:context];
[fetchRequest setEntity:entity];
// Create predicate
NSPredicate *pred = [NSPredicate predicateWithFormat:#"id == %#", user.id];
[fetchRequest setPredicate:pred];
NSArray *results = [context executeFetchRequest:fetchRequest error:&error];
if (error) {
// handle fetch error
} else {
user = [[User alloc] initWithEntity:entity insertIntoManagedObjectContext:context];
for (User *recordToDelete in results) {
[context deleteObject:recordToDelete];//record gets deleted here, which is fine
}
[context save:&error]; //this doesn't save the new entity that I passed in
if (error) {
// handle save error
NSLog(#"Whoops, couldn't save: %#", [error localizedDescription]);
}
}
[fetchRequest setEntity:entity];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"id=%#", 12345];
[fetchRequest setPredicate:pred];
NSArray *items = [context executeFetchRequest:fetchRequest error:&error];
for (User *testObject in items) {
NSLog(#"ID: %#, Name: %#, Email: %#, Created Date: %#", [testObject id], [testObject name], [testObject email], [testObject createdDate]);
}
}
The problem is that the above function deletes the existing record in the store, however, it fails to add the new entity that replaces it. How can I correct this?
Also, i think you don't clearly understand what is NSManagedObjectContext. It's something like in-memory object cache. So, if you create object in context, it is tied to context. Object has reference to context, so passing context with object is not necessary - object's context can be obtained from it. Also, contexts and objects are not thread-safe - you cannot pass managed objects between threads and use same context in different threads. Instead, you have to: 1) Create context for every thread
2) If you need to pass something between threads, pass object.objectId from one thread, and in another thread do [context objectWithID:]. It's extremly fast, efficient and safe.
You are not calling save method on managed object context. Call save method on managedObjectContext in which you are creating new object. [managedObjectContext save:nil];
Recmonded way is. First fetch object depending on number, and delete it. After that create managed object. At the end call Save on context.

fetch coredata relationship

I would like to know how to fetch the items from my coredata relation ship. I guess it should be a dictionary or arrays or something that gets returned so that you can have your one to many thing.
I am quite lost at this I know how to write/read single objects but this relationship stuff is abit confusing.
I think I have sucsessfully written a relationship to coredata however now I would like to be able to read it to see if I have it right.. I have started writing the method for this but have no idea what to actually do to get all of the information out.
this is the code i have so far.. for both read and write
- (void)writeFin:(NSArray *)recivedProjectData ItemsData:(NSArray *)itemsData {
// WRITE TO CORE DATA
NSManagedObjectContext *context = [self managedObjectContext];
for (NSDictionary *dict in recivedProjectData) {
project = [NSEntityDescription insertNewObjectForEntityForName:#"Project" inManagedObjectContext:context];
project.proNumber = [dict valueForKey:#"ProNumber"];
project.projectDescription = [dict valueForKey:#"Description"];
// project.items = [dict valueForKey:#""]; // this is the relationship for project
}
for (NSDictionary *dict in itemsData) {
items = [NSEntityDescription insertNewObjectForEntityForName:#"Items" inManagedObjectContext:context];
items.Number = [dict valueForKey:#"Number"];
items.Description = [dict valueForKey:#"Description"];
items.comment = [dict valueForKey:#"Comment"];
items.project = project; // this is the relationship for items
[project addItemsObject:items];
}
NSError *error = nil;
if (![__managedObjectContext save:&error]) {
NSLog(#"There was an error! %#", error);
}
else {
NSLog(#"created");
}
}
- (NSMutableArray *)readFin {
NSManagedObjectContext *context = [self managedObjectContext];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Project" inManagedObjectContext:context];
[fetchRequest setEntity:entity];
NSError *error;
NSMutableArray *projectDictionaryArray = [[NSMutableArray alloc] init];
NSArray *fetchedObjects = [context executeFetchRequest:fetchRequest error:&error];
for (ProjectList *projectList in fetchedObjects) {
NSMutableDictionary *tempProjectDictionaryArray = [[ NSMutableDictionary alloc] init];
[tempProjectDictionaryArray setObject:project.proNumber forKey:#"ProNumber"];
[tempProjectDictionaryArray setObject:project.description forKey:#"Description"];
[tempProjectDictionaryArray setObject:project.projectID forKey:#"ProjectID"];
[projectDictionaryArray addObject:tempProjectDictionaryArray];
}
return projectDictionaryArray;
}
So just o reiterate, I would like to know A, is my write method look okay? B, how do you fetch/read the relationship objects from core data.
any help would be greatly appreciated.
A relationship in Core Data isn't an object, its a property which happens to correspond to another object in your model rather than just being a dead end. You're already most of the way there as far as checking whether your relationships are ok as far as I can see -- what you need to do is add one more line in your projectList
[tempProjectDictionaryArray setObject: project.items forKey:#"items"];
the object that you will have added will be an NSSet. You can then check that things are as they should be with a loop like this after you've finished setting things up
NSSet itemsForProject = projectDictionaryArray[someIndex][#"items"]
for (Item* currItem in [itemsForProject allObjects]) {
//access some property of the current item to make sure you have the right ones -- \
description for example
NSLog(#"%#", item.description);
}

Resources