This how NSManagedObject is created
NSEntityDescription *entity = [NSEntityDescription entityForName:strEntityName inManagedObjectContext:managedObjContext];
NSManagedObject * managedObject = (NSManagedObject *)[[NSClassFromString(strEntityName) alloc] initWithEntity:entity insertIntoManagedObjectContext:managedObjContext];
//values are mapped into this object
Now save NSManagedObject to persistent store and fetching currently inserted object like this :
NSError *error;
BOOL isDone = [managedObjectContext save:&error];
//BOOL isDone = [managedObjectContext obtainPermanentIDsForObjects:[NSArray arrayWithObjects:tempManagedObject, nil] error:&error];
if (isDone && error == nil){
//fetch last inserted object here
//make fetch request
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:strEntityName];
//make query using fetch request in context
NSError *error;
NSArray *arrFetchRequest = [managedObjectContext executeFetchRequest:fetchRequest error:&error];
if (arrFetchRequest.count>0){
//This is last inserted object
NSManagedObject *managedObject = [arrFetchRequest lastObject];
return managedObject;
}
}
I have also refered Swift - How to get last insert id in Core Data, store it in NSUserDefault and predicate. But it will not have permanent object ID in NSManagedObject as we are saving temparory ID and fetching using that and we don't have permanent ID at all.
Canyone share any other options?
While inserting NSManagedObject in context, it will have temporaryID (this means it has not been saved to database).
So need to get permanentID (this means it has been saved to database) for NSManagedObject in context, use obtainPermanentIDsForObjects and same NSManagedObject will have permanentID
if (insertManagedObject)
{
NSLog(#"Before %# : %d",insertManagedObject.objectID,insertManagedObject.objectID.isTemporaryID);
//Obtain permanentID for object
NSError *error;
BOOL hasObtainedPermanentID = [managedObjectContext obtainPermanentIDsForObjects:[NSArray arrayWithObjects:insertManagedObject, nil] error:&error]; //;
//check if its done
if (hasObtainedPermanentID && error == nil){
NSLog(#"After %# : %d",insertManagedObject.objectID,insertManagedObject.objectID.isTemporaryID);
//check context has changes and is saved in context
if ([managedObjectContext hasChanges] && [managedObjectContext save:&error])
{
return insertManagedObject;
}
}
}
Related
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?
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
}
I'll try to expose my problem, because is a bit complex.
I use Core Data and I have a problem with the data stored.
When I use this code:
NSManagedObjectContext *context = [appDelegate managedObjectContext];
NSFetchRequest *request = [[NSFetchRequest alloc] initWithEntityName:#"ItemMessage"];
NSError *error = nil;
NSArray *results = [context executeFetchRequest:request error:&error];
NSMutableArray *values = [[NSMutableArray alloc] init];
if (error == nil) {
for (int i = 0; i<results.count; i++) {
NSLog(#"results %#",[results objectAtIndex:i]);
ItemMessage *itemMessage = [results objectAtIndex:i];
[values addObject:itemMessage];
}
ecc. the problem is that the value printed by NSLog is correct (the "results" contains something) but the itemMessage contains always 0 key/value pairs (it seems empty).
To understand what is the problem I went back and saw that in insertNewObjectForEntityForName I have also this problem, this is the code that I used when I save the messages data in Core Data:
for (id key in objectMessage) {
ItemMessage *itemmessage = [[ItemMessage alloc] init];
itemmessage.itemMessageId = [key objectForKey:#"itemMessageId"];
itemmessage.message = [key objectForKey:#"message"];
itemmessage.sender = [key objectForKey:#"sender"];
itemmessage.users = [key objectForKey:#"users"];
NSManagedObjectContext *context = [appDelegate managedObjectContext];
NSManagedObject *newMessage;
newMessage = [NSEntityDescription insertNewObjectForEntityForName:#"ItemMessage" inManagedObjectContext:context];
[newMessage setValue: itemmessage.itemMessageId forKey:#"itemMessageId"];
[newMessage setValue: itemmessage.message forKey:#"message"];
[newMessage setValue: itemmessage.sender forKey:#"sender"];
[newMessage setValue: itemmessage.users forKey:#"users"];
[context save:&error];
if (error != nil) {
NSLog(#"Coredata error");
}
The problem is that newMessage after the insertNewObjectForEntityForName and the setValue contains also 0 key/value pairs.
Can you help me?
You don't seem to insert the new managed objects correctly into the context.
It should be:
for (id key in objectMessage) {
NSManagedObjectContext *context = [appDelegate managedObjectContext];
ItemMessage *itemmessage = (ItemMessage*)[NSEntityDescription insertNewObjectForEntityForName:#"ItemMessage"
inManagedObjectContext:context];
itemmessage.itemMessageId = [key objectForKey:#"itemMessageId"];
itemmessage.message = [key objectForKey:#"message"];
itemmessage.sender = [key objectForKey:#"sender"];
itemmessage.users = [key objectForKey:#"users"];
}
//save your inserts
To create a class file for your managed objects you could:
Go to your model file (xcdatamodeld) ->
select an entity ->
from the menu select:
Editor-> Create NSManagedObjectSubclass -> select the entities your like class files for.
Now you will have managed objects you could access with ease (NSManagedObject subclass) and benefit from CoreData features.
When you insert to manage object contest you have to call save: method, also the saving method should looks something like that:
newMessage = [NSEntityDescription insertNewObjectForEntityForName:#"ItemMessage" inManagedObjectContext:context];
// 2
newMessage.property1 = self.firstNameTextfield.text;
newMessage.property2 = self.lastNameTextfield.text;
if (![context save:&error]) {
NSLog(#"Error: %#", [error localizedDescription]);
}
One of my coredata tables (uh, entities) should have ever only one row of data being stored. When the row doesn't exist yet it should be created and if it already exists, the same row should be overwritten (or edited) with new data.
Currently in my implementation a new row is always added to the entity (named 'TempNames'):
/* Store names data in temporary name table. */
TempNames *tempNames = [NSEntityDescription insertNewObjectForEntityForName:#"TempNames" inManagedObjectContext:context];
tempNames.namesData = tempNamesData;
Can anyone give me some hints what is needed to change it to my desired functionality? I suppose NSPredicate is required to achieve what I want?
UPDATED WORKING IMPLEMENTATION:
/* Convert names array into serializable data. */
NSData *tempNamesData = [NSKeyedArchiver archivedDataWithRootObject:names];
/* Store names data in temporary name table. */
TempNames *tempNames = nil;
NSError *error;
NSFetchRequest *req = [NSFetchRequest fetchRequestWithEntityName:#"TempNames"];
NSArray *records = [context executeFetchRequest:req error:&error];
/* Record already exists. */
if (records.count > 0)
{
tempNames = records.firstObject;
}
else
{
tempNames = [NSEntityDescription insertNewObjectForEntityForName:#"TempNames" inManagedObjectContext:context];
}
tempNames.namesData = tempNamesData;
[context save:&error];
If you insert a new entity every time, you are not doing an insert/update operation, it's always an insert operation, even if the data of that entity is the same. What you should do is first fetch if you have that data in your store and then decide if you need to insert or update it.
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:#"TempNames"];
NSError *error;
NSArray *entities = [context executeFetchRequest:request error:&error];
if (entities.count > 0) {
// You have already inserted the entity
}
else {
// Insert your new entity
...
// Save changes to the store
NSError *error;
[context save:&error];
}
Try it like this:
TempNames *tempNames = nil;
NSFetchRequest *appRequest = [NSFetchRequest fetchRequestWithEntityName:#"TempNames"];
NSArray *allNames = [context executeFetchRequest:appRequest error:nil];
if (allNames.count > 0) {
// your record exists
tempNames = allNames.firstObject;
} else {
tempNames = [NSEntityDescription insertNewObjectForEntityForName:#"TempNames" inManagedObjectContext:context];
}
tempNames.namesData = tempNamesData;
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.