Core Data saving to Only One Entity - ios

I'm a bit confused about saving entities using Core Data. I'm making a screen that will allow users to save their settings (contact information), which can be changed later if they wish.
From what I understand, my code below will save multiple entities each time the 'save' button is pressed.
- (IBAction)saveSettings:(id)sender {
AppDelegate *appDelegate =
[[UIApplication sharedApplication] delegate];
NSManagedObjectContext *context =
[appDelegate managedObjectContext];
NSManagedObject *userSettings;
userSettings = [NSEntityDescription
insertNewObjectForEntityForName:#"UserSettings"
inManagedObjectContext:context];
[userSettings setValue: _firstName.text forKey:#"firstName"];
[userSettings setValue: _lastName.text forKey:#"lastName"];
[userSettings setValue: _userEmail.text forKey:#"userEmail"];
[userSettings setValue: _zipCode.text forKey:#"zipCode"];
}
What I don't understand how to do is save one entity, and then change the values of the attributes later on whenever the user types in new values in the appropriate text fields and presses 'save'.

Yes - because you use insertNewObjectForEntityForName:, a new UserSettings object is created each time that method is run. What you probably want to do is to fetch the existing settings from the database, update your textFields with that data, present the view and let the user amend the details as necessary, and then (when they press the save button), save that data back to the database.
I would add userSettings as a property:
#property (strong, nonatomic) NSManagedObject *userSettings;
and in your method delete the declaration of userSettings, and the line where you use insertNewObjectForEntityForName.
Then create a new method to handle fetching the data from the database and assigning it to your textFields, as follows:
-(void)loadSettings {
AppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
NSManagedObjectContext *context = [appDelegate managedObjectContext];
NSFetchRequest *fetch = [NSFetchRequest fetchRequestWithEntityName:#"UserSettings"];
NSError *error;
NSArray *results = [context executeFetchRequest:fetch error:&error];
if (results == nil) {
// some error handler here
}
if ([results count] > 0) {
userSettings = [results lastObject];
_firstName.text = [userSettings valueForKey:#"firstName"];
_lastName.text = [userSettings valueForKey:#"lastName"];
_userEmail.text = [userSettings valueForKey:#"userEmail"];
_zipCode.text = [userSettings valueForKey:#"zipCode"];
} else {
// set your text fields to some defaults values??
}
}
Call this method when your view controller loads, in the viewDidLoad method. I've assumed that you will normally have only one UserSettings object (hence lastObject will be the only object!). If you could have many UserSettings objects, you would need to filter the fetch to get only the one you want. To do that you would need to set a predicate for the fetch - look at the documentation for NSPredicate.

You are actually overwriting those properties everytime you "set". The correct way to store individual properties is to assign them and save, like so:
AppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
NSManagedObjectContext *context = [appDelegate managedObjectContext];
NSManagedObject *userSettings;
userSettings = [NSEntityDescription insertNewObjectForEntityForName:#"UserSettings"
inManagedObjectContext:context];
userSettings.firstName = _firstName.text;
userSettings.lastName = _lastName.text;
userSettings.userEmail = _userEmail.text;
userSettings.zipCode = _zipCode.text;
NSError *error = nil;
if (![context save:&error]) {
NSLog(#"Error Saving: %#", error);
}

Related

How to update transformable attribute in core data

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?

Read specific data in table

Very basic iOS question. I have a table created (m_historytable) with four columns created in the form of subviews (counter, date, name, and result). Every time the app runs a new row is added to the top of the table. I need to read the most recent name added and pass it to a UILabel. I would expect the statement to be something like:
m_last_name.text = [NSString stringWithFormat:#"%d", ??? ];
My question is what I need to replace ??? with.
You need to be studying CoreData in more details. For getting data from your core data entries use the following code.
Create a function in your class to get context
- (NSManagedObjectContext *)managedObjectContext {
NSManagedObjectContext *context = nil;
id delegate = [[UIApplication sharedApplication] delegate];
if ([delegate performSelector:#selector(managedObjectContext)]) {
context = [delegate managedObjectContext];
}
return context;
}
Now to get data from core data entities
NSManagedObjectContext *context = [self managedObjectContext];
NSMutableArray *historyDataArray = [[NSMutableArray alloc]init];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:#"HistoryData"];
historyDataArray = [[context executeFetchRequest:fetchRequest error:nil] mutableCopy];
once you get the array, you can choose the most recent item like this
NSManagedObject *object = [historyDataArray objectAtIndex:historyDataArray.count-1];
Then you can pass any value to a UILabel as follows:
m_last_name.text = [[NSString stringWithFormat:#"%#",[object valueForKey:#"name"] ];
You can pass there current date with [NSString stringWithFormat:#"%#",[NSDate date]];
You need to replace with some integer value as %d indicates the integer. Refer below sample:-
int count=1;
m_last_name.text = [NSString stringWithFormat:#"%d",count];
For more refer this example

Deleting a record from Core Data

I'm having trouble deleting records from Core Data SQLite file. I want to be able to delete the corresponding record from my file when I delete a row from my table view.
Here is what I am doing after fetching all records into allContacts array
NSManagedObject *contactRecord = [allContacts objectAtIndex:arc4random() % allContacts.count];
self.managedObjectID = [contactRecord objectID];
Then called my method that prepares my contacts and then display them on the tableview.
When I delete a row from the table, I call this method
-(void)deleteContactFromFile:(contact *)deletedContact
{
NSLog(#"deleted Contact %#",deletedContact.personID);
AppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
NSManagedObjectContext *context = appDelegate.managedObjectContext;
[context deleteObject:[context objectWithID:self.managedObjectID]];
[context save:nil];
}
The funny thing is I get a random record deleted from my core data file, but not the one I selected. I don't know how to deal with ObjectID thing for deleting a specific NSManagedObject.
If my question is not clear enough please tell me to clarify more.
You should be using an NSFetchedResultsController. It will help you to associate every index path of your table view with a specific managed object. You then do not need to fetch all data and filter through them.
For example, if you have the index path and a fetched results controller it is as easy as
NSManagedObject *object = [self.fetchedResultsController objectAtIndexPath:indexPath];
NSManagedObjectContext *context = object.managedObjectContext;
[context deleteObject:object];
[context save:nil];
Note that you not need to go to your app delegate to get the managed object context.
Try this:
- (void)deleteContactFromFile:(contact *)deletedContact {
NSManagedObjectContext *context = [(AppDelegate *)[[UIApplication sharedApplication] delegate] managedObjectContext];
NSFetchRequest *fetchRequest = [NSFetchRequest new];
[fetchRequest setEntity:[NSEntityDescription entityForName:#"EntityName" inManagedObjectContext:context]];
NSError *error;
NSArray *rootArray = [context executeFetchRequest:fetchRequest error:&error];
for (NSManagedObject *object in rootArray) {
if ([context objectWithID:self.managedObjectID]) {
[context deleteObject:object];
}
}
}

Update CoreData object instead of inserting new object

I am trying to update a current CoreData object however when ever I use my write method its just adding another object to the database, so when I try to read the objects I get dozens of them back depending how long it's been used for.
Here is my code
#pragma mark -- Write
- (void)writeSeriesSearchObj:(NSMutableDictionary *)SearchDic Name:(NSString *)name
{
// WRITE TO CORE DATA
NSManagedObjectContext *context = [self managedObjectContext];
SeriesSearchObj *searchObj = [NSEntityDescription insertNewObjectForEntityForName:#"SeriesSearchObj" inManagedObjectContext:context];
if ([name isEqualToString:#"Code"]) {
searchObj.code = [SearchDic objectForKey:#"Code"];
} else if ([name isEqualToString:#"Name"]) {
searchObj.name = [SearchDic objectForKey:#"Name"];
} else if ([name isEqualToString:#"Model"]) {
searchObj.modelID = [SearchDic objectForKey:#"ModelID"];
}
NSError *error = nil;
[self.managedObjectContext save:&error];
// test
NSMutableArray *temptestA = [self readSearchObj];
NSLog(#"%#", temptestA);
}
I suspect I am going wrong using insertNewObjectForEntityForName however I am not sure how else to write this method in order for the same object to be updated every time?
SeriesSearchObj *searchObj = [NSEntityDescription insertNewObjectForEntityForName:#"SeriesSearchObj" inManagedObjectContext:context];
Will always return you a new object.
Use NSFetch to obtain the existing object and then update it.

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
}

Resources