Remove an Object/Row from a Core Data Entity - objective-c

There are a couple answers similar to this (Like deleting an entire entity). But none that have worked for me. I am just looking for simple way to fetch the object I want to delete using NSPredicate and then delete only that object. I only want to delete one object/row. It should be fairly simple code, nothing too complicated. I would provide my code but I am pretty sure it won't help because it isn't even close to being complete or even working.

EntityToDelete *entity = (EntityToDelete *)[NSEntityDescription insertNewObjectForEntityForName:#"EntityToDelete" inManagedObjectContext:self.managedObjectContext];
NSError *error;
[self.managedObjectContext deleteObject:entity];
if (![self.managedObjectContext save:&error]) {
}

Related

Keep getting "'executeFetchRequest:error: A fetch request must have an entity.'" Even though I clearly set an entity :\

And yes, I've set up all the proper entities in my xcdatamodelId. I've been using CoreData for months now and have never run into this problem just yet. Is there something else I'm possible missing?
This is my code to pull data.
- (NSArray *)getCDRuneSets {
NSError *error;
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Rune" inManagedObjectContext:context];
[fetchRequest setEntity:entity];
NSArray *cdRuneSets = [context executeFetchRequest:fetchRequest error:&error];
return cdRuneSets;
}
And I have an entity named "Rune" in my xcdatamodelId. Any ideas on what I could possibly be doing wrong? :\
Edit:
I also have my AppDelegate run this piece of code to instantiate the my CoreDataBank's (It's a StaticSingleton) context
CoreDataBank *bank = [CoreDataBank getBank];
[bank setContext:[self managedObjectContext]];
Another possible solution to this incase anyone finds it... Make sure your entity name in your model matches your specified entity in entityForName
I had an s in my model (as in plural) and not in my code :(

Updating core data entry

I'm working with core data to save scores for a game, and need help updating an entry when a score is beaten. This is where I'm at. I write the data the first time the app launches like this:
LevelData *levelOne = [NSEntityDescription insertNewObjectForEntityForName:#"LevelData" inManagedObjectContext:context];
levelOne.levelNum = #"1";
levelOne.topScore = #"0";
levelOne.isPassed = #"No";
if (![context save:&error]) {
NSLog(#"coudlnt save: %#", [error localizedDescription]);
}
I then read out the data at the end of the level like this:
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
fetchRequest.predicate = [NSPredicate predicateWithFormat:#"levelNum == 1"];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"LevelData" inManagedObjectContext:managedObjectContext];
[fetchRequest setEntity:entity];
NSError *error;
NSArray *fetchedObjects = [managedObjectContext executeFetchRequest:fetchRequest error:&error];
for (LevelData *info in fetchedObjects) {
NSLog(#"Is Passed: %#", info.levelNum);
NSLog(#"Top Score: %#", info.topScore);
NSLog(#"Is Passed: %#", info.isPassed);
}
What I'm stuck doing is updating the topScore entry and writing it back to the data store without creating a new entry, but updating the existing one.
And help/example would be so so helpful and much appreciated.
Thanks, Kyle
Well, updating the entry from the data store is easy. Fetch, update, save. As you seem to have a handle on. The real issue with core data is defining that initial data store. The design of core data does not bring in the concept of a database, so typically, one does not exist until the backend implements it for you (after your application calls its first save).
What I have seen developers typically do to work around this is set up AppDelegate to implement a quick and dirty hack: save an empty data base. They then comment out this code; retrieve the file from disk, rename it, modify it, add it to the project's support files.
Now, revisiting AppDelegate, they test on application launch the existence of the datastore on the client's machine. If none exists, they know to inject the default data store from their support files.. basically copying it to the appropriate path and renaming it appropriately as well.
This gets overly complicated with versioning. Another approach to this is to write a plist of the defaults into the main bundle and import this on the first launch and store one key in user defaults to let me know if this is necessary. It requires a little more boilerplate code, but it is a great way to get things set up. And you can encode versioning data to support upgrade merging a lot easier. If you can handle core data well, manipulating XML shouldn't be much more difficult.

Not getting data from Core Data

I am using Core Data to store some information for my app.
I have a .xcdatamodeld file containing 8 entities, and I extract them on different views.
In one of the viewControllers, I call three of them. Like this:
AppDelegate *appDelegate = (AppDelegate *) [[UIApplication sharedApplication]delegate];
managedObjectContext = appDelegate.managedObjectContext;
NSManagedObjectContext *moc = [self managedObjectContext];
NSEntityDescription *entiAll = [NSEntityDescription entityForName:#"AllWeapons" inManagedObjectContext:moc];
NSFetchRequest *frAll = [[NSFetchRequest alloc] init];
[frAll setEntity:entiAll];
NSError *error = nil;
arrAll = [moc executeFetchRequest:frAll error:&error];
displayArray = [[NSMutableArray alloc]initWithArray:arrAll];
NSEntityDescription *entiRange = [NSEntityDescription entityForName:#"WeaponsRanged" inManagedObjectContext:moc];
NSFetchRequest *frRanged = [[NSFetchRequest alloc] init];
[frRanged setEntity:entiRange];
NSError *errorRanged = nil;
arrRange = [moc executeFetchRequest:frRanged error:&errorRanged];
NSLog(#"%i, %i", [arrRange count], [[moc executeFetchRequest:frRanged error:&errorRanged] count]);
NSEntityDescription *entiMelee = [NSEntityDescription entityForName:#"WeaponsMelee" inManagedObjectContext:moc];
NSFetchRequest *frMelee = [[NSFetchRequest alloc] init];
[frMelee setEntity:entiMelee];
NSError *errorMelee = nil;
arrMelee = [moc executeFetchRequest:frMelee error:&errorMelee];
NSLog(#"%i, %i", [arrMelee count], [[moc executeFetchRequest:frMelee error:&errorMelee] count]);
The problem is that the middle one (the one filling the arrRange-array) doesn't work..
arrAll logs out with all correct data, arrMelee logs out with all the correct data (x4 for some reason, don't know if this is related :S), but arrRange logs out as an empty array.
[arrRange count]; gives me 0, even though I know there is lots of data there.
I ran this code on the simulator, and found the .sqlite file, opened it in Firefox's SQLite Manager, and saw the correct data, 40 rows.
I went into the appDelegate, where I fill the CoreData when necessary, and saw that the method which downloads the data in JSON-format successfully sends it to the sqlite aswell.
Here I fill the CoreData with data from the json:
[self deleteAllObjects:#"WeaponsRanged"];
NSManagedObjectContext *context = [self managedObjectContext];
for(NSDictionary *item in jsonWeaponRanged)
{
WeaponsRanged *wr = [NSEntityDescription insertNewObjectForEntityForName:#"WeaponsRanged"
inManagedObjectContext:context];
///***///
wr.recoil = [item objectForKey:#"Recoil"];
///***///
NSError *error;
if(![context save:&error])
NSLog(#"%#", [error localizedDescription]);
}
And if I here do NSLog(#"%# - %#", wr.recoil, [item objectForKey:#"Recoil"]); I get the correct data. (Same data on both)
So. The correct data is obviously in the core. But my NSFetchRequest or something is failing. I am pretty noob at Objective-C, so it might be my bad code-grammar striking again. I realize I should use things again etc, not creating new objects all the time.. But cmon, this is my first app.. And if that is actually the problem, I might learn. But I'm stuck.
SOMETIMES I get data, sometimes I don't. It's weird. I re-launched the app, and got data from it, and now I don't.. I haven't found a pattern yet..
Anyone?
Or is there another way to request data from the entity?
I have some suggestions, too big for a comment.
1) after you create the WeaponsRanged, try reading them back:
for(NSDictionary *item in jsonWeaponRanged)
{
WeaponsRanged *wr = [NSEntityDescription insertNewObjectForEntityForName:#"WeaponsRanged"
inManagedObjectContext:context];
NSLog(#"IS WR Realized? %#", wr ? #"YES" : #"NOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO WR");
///***///
wr.recoil = [item objectForKey:#"Recoil"];
///***///
NSError *error;
if(![context save:&error])
NSLog(#"%#", [error localizedDescription]);
}
// Now lets see if we can retrieve them:
{
NSEntityDescription *entiRange = [NSEntityDescription entityForName:#"WeaponsRanged" inManagedObjectContext:context];
NSFetchRequest *frRanged = [[NSFetchRequest alloc] init];
[frRanged setEntity:entiRange];
NSError *errorRanged = nil;
arrRange = [context executeFetchRequest:frRanged error:&errorRanged];
NSLog(#"Wrote %i items, read back %i items", [jsonWeaponRanged count], [arrRange count] );
}
2) In the viewController reading WeaponsRanged, add an assert before the fetch on mod:
NSLog(#"IS moc set? %#", moc ? #"YES" : #"NOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO MOC");
EDIT:
3) Spread statements everywhere you access the MOC:
assert([NSThread isMainThread]);
[If you haven't used asserts before google and read up on the topic. These are a powerful tool for developers to find out about potential problems way before they manifest themselves in the gui or elsewhere. They are normally compiled out for release/distribution builds.]
This will force an exception if the thread is not the main thread, and then let you track down the reason by following the stack trace.
Nevermind! It was my own damn fault (yet again..).
The problem occured before the code I presented, and it turns out the data was never in the .sqlite-file when the problem was present.
This is what I had:
I collected data from the internet through json-request. I have told the app to check the "version" of the data through the internet, and if the data is outdated, then re-download it.
First, I download all data, then I add them to their own entity in Core Data. After downloading, I clear the current Core Data entity of the downloaded data. So on the top of each add-method it said i.e [self deleteAllObjectsOfEntity:#"WeaponsRanged"];, My whole problem was that in the addMelee-method, it ALSO said [self deleteAllObjectsOfEntity:#"WeaponsRanged"]; instead of #"WeaponsMelee", thus deleting all ranged weapons, and later adding melee to the melee entity. And that also proves that the other problem I mentioned of arrMelee logging out four times as much data as it should was caused by this.
The reason it sometimes worked was that the downloading is not happening in any ordered mode. So the addRanged was sometimes called before the addMelee. If ranged comes first, it clears the arrRanged, and fills it up with correct data, and THEN melee comes, and clears it out. When melee was called first, it cleared arrRanged and filled additional data to arrMelee, and THEN ranged comes and tries to clear an empty entity, and then fills it up with correct data.
The solution was obviously to change the entity deleted when adding it, as it was the wrong one.
Sorry.... :)

Possible issue with fetchLimit and fetchOffset in a Core Data query

I have a very sporadic bug with a Core Data query containing fetchLimit and fetchOffset. Once in a long while (I've seen it happen once, as has another tester), the fetchOffset seems to be ignored. The query looks like this:
NSFetchRequest *fetch = [[NSFetchRequest alloc] initWithEntityName:#"MyEntity"];
NSSortDescriptor *dateDescriptor = [[NSSortDescriptor alloc] initWithKey:#"timestamp" ascending:NO];
NSArray *sortDescriptors = [NSArray arrayWithObject:dateDescriptor];
[fetch setSortDescriptors:sortDescriptors];
fetch.fetchOffset = 500;
fetch.fetchLimit = 1;
NSError *error = nil;
NSArray *objects = [self.managedObjectContext executeFetchRequest:fetch error:&error];
if (objects.count) {
MyEntity *objectAtLimit = [objects objectAtIndex:0];
}
This almost always returns the 501st object as desired, but on those two occasions where it broke it returned the first object.
The query is never run unless there are >500 rows in the database. I'm using iOS5. The managedObjectContext has a mainQueueConcurrencyType.
It seems to be the same behavior as reported in this question: Paging results from Core Data requests, which was never resolved (or at least not on list.) In that case the fetchOffset appeared to be either ignored or respected based on the data model being tested against.
I'm probably going to rewrite the query without the fetchOffset, just in case that is the problem, since the performance shouldn't be an issue. But I'm wondering if anyone has thoughts about where the bug might be here.
Ran across a similar problem this morning and noticed if my NSManagedObjectContext has unsaved changes that the fetchOffset may be ignored for whatever reason. After saving the context the fetchOffset is interpreted correctly.
The problem indeed seems the be connected with unsaved changes in the NSManagedObjectContext. If you set the includesPendingChanges property to false the NSFetchRequest with limit + offset will work as expected.
fetchRequest.includesPendingChanges = false

Traversing multiple Core Data objects using NSPredicate

I am having a problem with a Core Data model in Cocoa. It's probably a fairly basic problem. A section of my Core Data model is shown below. Given the value of a cell property in the OutputCell entity, I want to return the relevant HistogramBar.
I'm using the following Predicate but it just returns an empty array. I've managed to get it working using the Histogram entity but I don't seem to be able to traverse from HistogramBar through Histogram and on to OuputCell. The predicate I'm using is:
NSEntityDescription *histogramBarEntityDescription = [NSEntityDescription entityForName:#"HistogramBar"
inManagedObjectContext:[theDocument managedObjectContext]];
NSFetchRequest *histogramBarRequest = [[[NSFetchRequest alloc] init] autorelease];
[histogramBarRequest setEntity:histogramBarEntityDescription];
NSPredicate *histogramBarPredicate = [NSPredicate predicateWithFormat:#"(histogram.outputCell.cell = %#)", theOutputCell];
[histogramBarRequest setPredicate:histogramBarPredicate];
NSError *histogramBarError = nil;
NSArray *histogramsArray = [[theDocument managedObjectContext] executeFetchRequest:histogramBarRequest
error:&histogramBarError];
Thankyou for the help.
My problem is solved, a rogue comment in the code prevented the HistogramBar entities being created. In this instance detailed checking of some NSLog's helped spot the problem.