I have a database (coredata) with 2 entities (with no relationships)... Inserting and fetching works well in my case.. But deleting part is troubling me a lot.. The object in one entity got deleted but others are nt..
Here is my code:
-(void)deleteObject:(NSString *)entityDescription //entityDescription get entity name
{
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:entityDescription inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
NSError *errors;
NSArray *items = [self.managedObjectContext executeFetchRequest:fetchRequest error:&errors];
NSManagedObject *managedObject=[finalArray objectAtIndex:currentImageIndex];
for (int i=0;i<[items count];i++)
{
if ([managedObject isEqual:[items objectAtIndex:i]])
{
[self.managedObjectContext deleteObject:managedObject];
}
}
NSLog(#"%# object deleted", entityDescription);
NSNotificationCenter *nc1=[NSNotificationCenter defaultCenter];
[nc1 addObserver:self selector:#selector(deleteCheck:) name:NSManagedObjectContextObjectsDidChangeNotification object:self.managedObjectContext];
NSError *error;
if (![self.managedObjectContext save:&error])
{
NSLog(#"error occured during save = %#", error);
}
else
{
NSLog(#"deletion was succesful");
}
This my code I follow the same method for deleting the objects from other entities...The entitydescription gets the different entity name from another method... Itz working well for one entity and not for another...But I'm getting the managedObjectContext deletion successful message(bt not deleted frm DB).. How can I solve this?
A couple of things you might do to narrow it down:
NSArray *items = [self.managedObjectContext executeFetchRequest:fetchRequest error:&errors];
// Add this to see that you are returning items from your fetch
NSLog(#"%i objects returned in items array",items.count);
NSManagedObject *managedObject=[finalArray objectAtIndex:currentImageIndex];
// Add this to see what your fetch is returning
NSLog(#"Fetch returned %#", managedObject);
for (int i=0;i<[items count];i++)
{
// Add this to see for yourself if it is equal to managedObject
NSLog(#"Testing: %#",[items objectAtIndex:i]);
if ([managedObject isEqual:[items objectAtIndex:i]])
{
[self.managedObjectContext deleteObject:managedObject];
// Move this to here so you know each time an object is deleted
NSLog(#"%# object deleted", entityDescription);
}
}
I suspect that you want to test a property of the object, not that the objects are equal. You were reporting "object deleted" when loop finished regardless of whether or not you had deleted anything. If you meant to test a property of the managed object change your test to:
If ([managedObject.propertyToTest isEqual:[items objectAtIndex:i]])
Or a property of each item:
If ([managedObject isEqual:[items objectAtIndex:i].propertyToTest])
Related
I work on app that use Core data to save data in local device. In Core data i have save data in array using Transformable format but, i don't know how to update particular values in the array.
My code for update Array is here
NSManagedObjectContext *context = [self managedObjectContext];
NSManagedObject *user = [NSEntityDescription insertNewObjectForEntityForName:#"Type" inManagedObjectContext:context];
NSError *error = nil;
//Set up to get the thing you want to update
NSFetchRequest * request = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Type"inManagedObjectContext:context];
[request setEntity:entity];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"businessTypes == %#", #"Others"];
[request setPredicate:predicate];
AppDelegate *app = (AppDelegate*)[[UIApplication sharedApplication]delegate];
NSArray *results = [self.managedObjectContext 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...
} else {
// At least one object saved. There should be only one
// so use the first...
user = [results lastObject];
[user setValue:#"Management" forKey:#"businessTypes"];
}
}
if (![self.managedObjectContext save:&error]) {
//Handle any error with the saving of the context
}
else{
[app saveContext];
NSLog(#"update value successfully");
}
and below is my save array in core data:
{
businessTypes = (
"Social Bussiness",
Marketing,
Transports,
Others
);
},
so i want to update "Others" to "Management" in the array.
When i run this code i have no error but i don't update particular value at index array.
thanks to help me.
Perhaps you are confusing your entities. You fetch an entity called Type but you are calling the object user, indicating that perhaps you wanted to fetch a user that has a certain business type.
If each user has only one "business type", you do not need a Type entity, just a string attribute for the User entity.
If each user can have more than one business type, you should have an entity Type with a name attribute that includes one term indicating the business type, and it should be modeled as a many-to-many relationship.
User <<--------->> Type
To set all types that are now called "Other" to "Management", you would fetch the Type with name "Other", change it and save. To only change one of a user's business types from "Other" to "Management", you would: fetch the user, remove the "Other" type, fetch the "Management" type, add it to the user and save.
If your businessTypes attribute is supposed to be a transformable array with hard-coded strings, you should probably change the data model as described above. You will have much more flexibility and power for searching and handling the data with a clean Core Data model.
You have to modify your update function like this code then you will get your required output
NSManagedObjectContext *context = [self managedObjectContext];
NSError *error = nil;
NSFetchRequest * request = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Type"inManagedObjectContext:context];
[request setEntity:entity];
request.propertiesToFetch= #[ #"businessTypes"];
AppDelegate *app = (AppDelegate*)[[UIApplication sharedApplication]delegate];
NSArray *results = [self.managedObjectContext 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...
} else {
int loopCount = (int)results.count;
Type* entityType = nil;
for (int index=0; index<loopCount; index++) {
entityType = (Type*)results[index];
if (entityType.businessTypes!=nil) {
NSUInteger reqIndex = [entityType.businessTypes indexOfObject:#"Others"];
[entityType.businessTypes replaceObjectAtIndex:reqIndex withObject:#"Management"];
[entityType setValue:entityType.businessTypes forKey:#"businessTypes"];
}
}
}
if (![self.managedObjectContext save:&error]) {
//Handle any error with the saving of the context
}
else{
[app saveContext];
NSLog(#"update value successfully");
}
Instruments shows the following code leaks, if I comment out this code there is no leak.
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
// Edit the entity name as appropriate.
NSEntityDescription *entity = [NSEntityDescription entityForName:USER_CORE_DATA inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
NSPredicate *predicte = [NSPredicate predicateWithFormat:#"username == %#", [[User defaultManager] savedUsername]];
[fetchRequest setPredicate:predicte];
// set any predicates or sort descriptors, etc.
// execute the request
[self.managedObjectContext executeFetchRequest:fetchRequest onSuccess:^(NSArray *results) {
} onFailure:^(NSError *error) {
NSLog(#"Error fetching: %#", error);
}];
[fetchRequest release];
Specifically instruments says this line in the code above:
[self.managedObjectContext executeFetchRequest:fetchRequest onSuccess:^(NSArray *results)
It appears to be a leak with fetchRequest and/or the block. Any help will be appreciated, and thanks in advance.
It appears executeFetchRequest:onSuccess:onFailure: is a function you have defined in NSManagedObjectContext category. Ensure the NSArray object instance that you are passing to the onSuccess block is autoreleased.
Actually it turned out that StackMob had a leak in their code, I downloaded there source and fixed it.
- (NSString *)primaryKeyField
{
NSString *objectIdField = nil;
// Search for schemanameId
objectIdField = [[self SMSchema] stringByAppendingFormat:#"Id"];
if ([[[self entity] propertiesByName] objectForKey:objectIdField] != nil) {
return objectIdField;
}
objectIdField = nil; // This line was missing and causing a leak
// Search for schemaname_id
objectIdField = [[self SMSchema] stringByAppendingFormat:#"_id"];
if ([[[self entity] propertiesByName] objectForKey:objectIdField] != nil) {
return objectIdField;
}
objectIdField = nil; // This line was missing and causing a leak
// Raise an exception and return nil
[NSException raise:SMExceptionIncompatibleObject format:#"No Attribute found for `entity %# which maps to the primary key on StackMob. The Attribute name should match one of the following formats: lowercasedEntityNameId or lowercasedEntityName_id. If the managed object subclass for %# inherits from SMUserManagedObject, meaning it is intended to define user objects, you may return either of the above formats or whatever lowercase string with optional underscores matches the primary key field on StackMob.", [[self entity] name], [[self entity] name]];`
Hi i am saving values coming from a database in a MultableArray and then in CoreData:
NSMultableArray *mutary = [[NSMultableArray alloc] init];
NSManagedObjectContext *context = [app managedObjectContext];
for(int n=0; n<[AttributeArray count]; n++)
{
[mutary addObject:[[AttributeArray objectAtIndex:n] objectForKey:#"AttributName"]];
NSLog(#"%#", mutary);
}
attributeString = [mutary componentsJoinedByString:#","];
raume = [NSEntityDescription insertNewObjectForEntityForName:#"Raum" inManagedObjectContext:context];
raume.raumattribut = attributeString;
if (![context save:&error]) {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
NSLog output for the MultableArray is:
2012-06-20 17:21:00.047 book-app[31984:15803] (
A7OVERHEAD,
Beamer
)
So far its working correct. The two expected values from the database are now in the Array.
Now i am fetching these attributes from CoreData:
NSManagedObjectContext *context = [app managedObjectContext];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Raum" inManagedObjectContext:context];
[request setEntity:entity];
NSError *error = nil;
NSArray *events = [context executeFetchRequest:request error:&error];
for (Raum *att in events)
{
stringAttribute = [[events valueForKey:#"raumattribut"] componentsJoinedByString:#","];
NSLog(#"ATTRIBUTE: %#", stringAttribute);
}
}
So far so good. But if i look now on my NSLog output:
2012-06-20 17:21:00.055 book-app[31984:15803] ATTRIBUTE: <null>,A7OVERHEAD,Beamer
CoreData is returning and then the two values. Where is that coming from?
Can someone help?
Thanks in advance
EDIT: After some investigating and clarifying (see comments), here is my answer:
Calling valueForKey on an NSArray returns an array where each element is created by calling valueForKey on each member of the original array. This means your output indicates that you have 3 total objects returned by your fetch request, and the first one does not have it's raumattribut attribute set.
PREVIOUS ANSWER:
You are calling valueForKey on an instance of NSArray (the query results). Perhaps you need to get objectAtIndex:0 first use the variable you are iterating the values on? valueForKey is likely returning nil.
stringAttribute = [[att valueForKey:#"raumattribut"] componentsJoinedByString:#","];
or similar.
EDIT:
At first I missed the for loop in the second part, I have edited the above code sample to use the attr, which
I'm implementing a car manager for an iPhone app and I have trouble saving a new car. So I have a "Car" entity (and per-loaded DB), containing multiples attributes. I have 2 booleans "saved" and "selected" to track which car the user added to his list (if added, saved = 1) and which car is currently selected. So when I create a new car, I deselect the old one (selected=0), and want to modify the new car to set its attributes saved=1 and selected=1.
Here is my functions:
- (IBAction)save
{
// Disable the previous car selection.
[self deselectCurrentSelectedCar];
// Add new car as saved and selected.
[self saveAndSelectNewCar];
// Call the delegate to dismiss the modal view.
[_delegate dismissAndSave];
}
- (void)deselectCurrentSelectedCar
{
// Fetched saved car.
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Car" inManagedObjectContext:[self managedObjectContext]];
[fetchRequest setEntity:entity];
// Set predicate and sort orderings...
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"selected = 1"];
[fetchRequest setPredicate:predicate];
// Execute the fetch -- create a mutable copy of the result.
NSError *error = nil;
NSMutableArray *mutableFetchResults = [[self.managedObjectContext executeFetchRequest:fetchRequest error:&error] mutableCopy];
if (mutableFetchResults == nil) {
// Handle the error.
NSLog(#"[AddCarViewController] deselect car: car not found.");
}
else {
// Get car and assign new selected value.
Car *carToSave = (Car *)[mutableFetchResults objectAtIndex:0];
[carToSave setSelected:[NSNumber numberWithInt:0]];
// Save the car.
NSError *error = nil;
if (![self.managedObjectContext save:&error]) {
// Handle the error.
NSLog(#"[AddCarViewController] deselect car: error saving car.");
}
else {
NSLog(#"[AddCarViewController] deselect car: car saved.");
}
}
// Memory management.
[fetchRequest release];
[mutableFetchResults release];
}
- (void)saveAndSelectNewCar
{
// Get the car, and pass to the delegate the new settings.
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Car" inManagedObjectContext:[self managedObjectContext]];
[fetchRequest setEntity:entity];
// Set predicate and sort orderings...
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"(year=%#) AND (make=%#) AND (model=%#) AND (d=%#) AND (n=%#)", _car.year, _car.make, _car.model, _car.d, _car.n];
[fetchRequest setPredicate:predicate];
// Execute the fetch -- create a mutable copy of the result.
NSError *error = nil;
NSMutableArray *mutableFetchResults = [[self.managedObjectContext executeFetchRequest:fetchRequest error:&error] mutableCopy];
if (mutableFetchResults == nil) {
// Handle error.
NSLog(#"[AddCarViewController] save and select: can't save the new car");
}
else {
// Get the car selected.
Car *selectedCar = (Car *)[mutableFetchResults objectAtIndex:0];
[selectedCar setValue:[NSNumber numberWithInt:1] forKey:#"selected"];
[selectedCar setValue:[NSNumber numberWithInt:1] forKey:#"saved"];
// Save the car.
NSError *error = nil;
if (![self.managedObjectContext save:&error]) {
// Handle the error.
NSLog(#"[AddCarViewController] save and select: error saving car.");
}
else {
NSLog(#"[AddCarViewController] save and select: car saved.");
}
// Add car to delegate.
[EcoAppAppDelegate setUserCar:selectedCar];
}
// Memory management.
[fetchRequest release];
[mutableFetchResults release];
}
And I have this log all the time: "error saving car." on both functions. So there is definitely something wrong.
Also, it's pretty anoying to fetch the car I want to modify it, instead of doing right away an update. If there another please tell me!
Thanks.
In your header file, you should set up a mutable array for your cars.
NSMutableArray *carArray;
and
#property (nonatomic, retain) NSMutableArray *carArray;
Then make sure to synthesize it in your implementation file. Then when you fetch from your managed object context, you can set your array with the contents returned
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Car" inManagedObjectContext:[self managedObjectContext]];
[fetchRequest setEntity:entity];
// Execute the fetch -- create a mutable copy of the result.
NSError *error = nil;
NSMutableArray *mutableFetchResults = [[self.managedObjectContext executeFetchRequest:fetchRequest error:&error] mutableCopy];
if (mutableFetchResults == nil) {
// Handle the error.
NSLog(#"[AddCarViewController] deselect car: car not found.");
} else {
[self setCarArray:mutableFetchResults];
}
Doing this will hold onto all the objects in your managed object context, so when you want to modify a Car, you can find it there instead of fetching again. If you need to sort it, you can apply a sort descriptor initialized with one of your attributes as a key. For example:
NSSortDescriptor *sorter = [NSSortDescriptor sortDescriptorWithKey:#"selected" ascending:YES];
[carArray sortUsingDescriptors:[NSArray arrayWithObject:sorter]];
This will sort the Cars based on whether they are selected or not.
As for why you're getting an error, that could be one of many reasons. Typically the MOC will fail to save if one of your attributes is set to nil, so that might be the cause. You can get some detail from it if you set something like the following up
if (![self.managedObjectContext save:&error]) {
NSLog(#"failed with error %#", error);
}
This will return the actual error you ran into. It would also be a good idea to set up a log to make sure you have Car specified. Something like NSLog(#"selected car %#", carToSave); just to be safe
Ok, I thought I had this but I am not getting the results that I am expecting. Hopefully someone can help.
I have two entities Person and Timesheet with one attribute to-many relationship:
Person.timesheet<--->>Timesheet.user.
The code below works but when I try to add a second timesheet entry it seems to override the first?
I have looked at the Apple Docs and they are a little vague on this subject.
//Add
NSManagedObjectContext *context = self.managedObjectContext;
Person *personAdded = [NSEntityDescription insertNewObjectForEntityForName:#"Person" inManagedObjectContext:context];
Timesheet *timesheet = [NSEntityDescription insertNewObjectForEntityForName:#"Timesheet" inManagedObjectContext:context];;
timesheet.time = #"10:00 Friday";
timesheet.timestamp = [NSDate date];
NSSet *timesheetSet = [NSSet setWithObject:timesheet];
personAdded.name = #"Darren";
personAdded.job = #"Job to be Done";
personAdded.timesheet = timesheetSet;
NSError *error = nil;
[context save:&error];
if (error) {
NSLog(#"[ERROR] COREDATA: Save raised an error - '%#'", [error description]);
}
NSLog(#"[SUCCESS] COREDATA: Inserted new User to database!");
// Load
NSEntityDescription *personEntity = [NSEntityDescription entityForName:#"Person" inManagedObjectContext:context];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity: personEntity];
error = nil;
NSArray *results = [context executeFetchRequest:request error:&error];
if (!results || error) {
NSLog(#"[ERROR] COREDATA: Fetch request raised an error - '%#'", [error description]);
[request release];
}
NSLog(#"Results: %#",results);
Person *firstUser = [results objectAtIndex:0];
NSLog(#"First User's name: %#",firstUser.name);
NSLog(#"First User's time %#",[[firstUser.timesheet anyObject] valueForKeyPath:#"timestamp"]);
I am wondering if it could be because I am actually setting the Person.timesheet key with the NSSet and not the actual Table? OR could it be that I am not calling the results correctly?
Thanks,
Darren
You should have a generated "CoreDataGeneratedAccessors" method in your Person.h file which gives you a method
[personAdded addTimesheet:timesheetSet];
which creates the links for you ?
Core Data normally generates an add and a remove method for any relationships you define.
Post your person.h file if this is not clear.