Delete all records in NSManagedObjectContext - cocoa

Is there a way to delete all the records from an NSManagedObjectContext?
I'm using the following code to insert data:
NSManagedObjectContext * context = [[NSApp delegate] managedObjectContext];
NSManagedObject * basket = nil;
basket = [NSEntityDescription insertNewObjectForEntityForName:#"ShoppingBasket"
inManagedObjectContext: context];
[basket setValue:[firstSelectedObject valueForKey:#"accessoryID"]
forKey: #"accessoryID"];
How do I delete all the records? I want something that's like the "remove:" function, but to remove everything.

To delete all instances of a given entity (we'll use your ShoppingBasket), you can simply fetch all baskets then delete them. It's just a few lines of code:
NSManagedObjectContext * context = [self managedObjectContext];
NSFetchRequest * fetch = [[[NSFetchRequest alloc] init] autorelease];
[fetch setEntity:[NSEntityDescription entityForName:#"ShoppingBasket" inManagedObjectContext:context]];
NSArray * result = [context executeFetchRequest:fetch error:nil];
for (id basket in result)
[context deleteObject:basket];
The alternative in a non-document-based app is to shut down your connection to the data store, delete the actual file, then reconnect (the template code that comes with a standard Core Data project will automatically create the file if it's absent). You then have a brand new, empty store.
Note, the code example ignores any possible error. Don't do that. :-)

A much quicker way would be to just remove store entirely. This way you're not wasting any time fetching objects, or enumerating through them as the other answer does.
NSError *error;
NSURL *applicationDocumentsDirectory = [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
NSURL *storeURL = [applicationDocumentsDirectory URLByAppendingPathComponent:#"MyCDStore.sqlite"];
[[NSFileManager defaultManager] removeItemAtPath:storeURL.path error:&error];
Don't forget to re-create it after you have deleted it.

Related

Use data from two (or three) entities in tableview

I have an app that stores information for golf rounds. I have three Entities that I am persisting data to: Rounds, Courses, and Tees. I structured the schema in this way because there is a two-many relationship between Rounds and Courses, and a Course can have multiple Tees (blue, white, gold, etc.)
I have a UITableview that I would like to display the results of each round in. However, I would like to display data from the Rounds Entity as well as the Courses Entity. Ideally I would also like to display the Tee for that round as well,but it's not a priority.
My question is, how do I use a FetchResultsController to get data from the three entities and display it in a single cell of a UITableview?
Here is how I am saving the data to the Entities:
HandicapAppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
NSManagedObjectContext* context = [appDelegate managedObjectContext];
NSEntityDescription *roundsEntity = [NSEntityDescription entityForName:#"Rounds" inManagedObjectContext:context];
NSFetchRequest *request =[[NSFetchRequest alloc]init];
[request setEntity:roundsEntity];
Rounds * rounds = [NSEntityDescription insertNewObjectForEntityForName:#"Rounds" inManagedObjectContext:context];
[rounds setValue:score forKey:#"roundScore"];
[rounds setValue:date forKey:#"roundDate"];
[rounds setValue:differential forKey:#"roundDifferential"];
Courses * courses = [NSEntityDescription insertNewObjectForEntityForName:#"Courses" inManagedObjectContext:context];
[courses setValue:rating forKey:#"courseRating"];
[courses setValue:slope forKey:#"courseSlope"];
[courses setValue:courseName forKey:#"courseName"];
rounds.courses = courses;
Tee * tee = [NSEntityDescription insertNewObjectForEntityForName:#"Tee" inManagedObjectContext:context];
[tee setValue:teeColor forKey:#"teeColor"];
courses.tees = tee;
NSError *error;
[context save:&error];
And then this is my FetchedResultsController
if (_fetchedResultsController != nil)
{
return _fetchedResultsController;
}
// Create the fetch request for the entity.
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
fetchRequest.entity = [NSEntityDescription entityForName:#"Rounds" inManagedObjectContext:self.managedObjectContext];
// Set the batch size
[fetchRequest setFetchBatchSize:20];
// Set the sort descriptor
NSSortDescriptor * date = [[NSSortDescriptor alloc] initWithKey: #"roundDate"
ascending: NO];
NSArray * sortDescriptors = [NSArray arrayWithObjects: date, nil];
fetchRequest.sortDescriptors = sortDescriptors;
// Initialize fetched results controller - creates cache
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc]
initWithFetchRequest: fetchRequest
managedObjectContext: self.managedObjectContext
sectionNameKeyPath: nil
cacheName: #"Master"];
aFetchedResultsController.delegate = self;
self.fetchedResultsController = aFetchedResultsController;
// handle errors
NSError *error = nil;
if (![self.fetchedResultsController performFetch:&error]) {
/*
Replace this implementation with code to handle the error appropriately.
abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
*/
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
return _fetchedResultsController;
How do I fetch the course name and tee (this will be the tee color) for the round in the tableview cell?
Thanks!!
Just follow the relationships. If you've got a one-to-many relationship between rounds and courses, you'd need to have a relationship on courses like rounds and the inverse relationship on rounds like course. So, if you've generated subclasses for your entities, you'd access the course name like this:
NSString *courseName = round.course.name;

Fetch update in Cocoa

this is the code in my AppDelegate.m:
-(IBAction)fetch:(id)sender{
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Foo" inManagedObjectContext:[self managedObjectContext]];
[fetchRequest setEntity:entity];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"title == 'some title'"];
[fetchRequest setPredicate:predicate];
NSError *error = nil;
NSArray *fetchedObjects = [[self managedObjectContext] executeFetchRequest:fetchRequest error:&error];
if (fetchedObjects == nil) {NSLog(#"Error: %#", error);}
NSMutableArray *fooArray = [[NSMutableArray alloc]init];
for (Foo *f in fetchedObjects) {
//here another fastenum for a to-many relationship
for(Bar *b in f.relationship){
[fooArray addObject:b.title];
}
}
Everytime I perform the fetch action, even if I've changed the app.storedata file via UI and checked the changes in finder, the result is always the same until i quit the application. After a restart, the fetch result is up to date and aligned with the app.storedata file. The fooArray count is always the same, regardless if I add some entries in the entities and coredata save everything.
I've tried with [fetchRequest setIncludesPendingChanges:YES] but it doesn't affect the behaviour.
How to update the fetch result while the app is running?
UPDATE: i've "solved" the problem with this workaround:
-(IBACTION)fetch:(id)sender{
_managedObjectContext = nil;
_persistenStoreCoordinator = nil;
//rest of the code...
Is this workaround a final solution? Is there a more "correct" way to solve this problem?

A method for 'Save As...' in Core Data app

CD's been an enormous learning curve for me and there's still a bit for me to go, but any help on the following could enable me to lift the current weight on my shoulders!
I'm trying to write a method that implements a "Save As.." for the user in my CD app.
So far I've got:
[saveAsPanel beginSheetModalForWindow:window completionHandler:^(NSInteger userResult)
{
if (userResult == NSOKButton) {
NSPersistentStoreCoordinator *psc = [self persistentStoreCoordinator];
NSURL *oldURL = [self URLOfInternalStore]; //returns the current store's URL
NSURL *newURL = [saveAsPanel URL];
NSError *error = nil;
NSPersistentStore *oldStore = [psc persistentStoreForURL:oldURL];
NSPersistentStore *sqLiteStore = [psc migratePersistentStore:oldStore
toURL:newURL
options:nil
withType:NSXMLStoreType
error:&error];
}
}];
Unfortunately, I just get the error:
Object's persistent store is not reachable from this NSManagedObjectContext's coordinator.
Should I 'remove' and then 'addPersistentStore...' to update it to the new URL? The doc's seem to suggest that all will be handled with in the 'migrate' method.
Thanks in advance!
Edit:
Ok, well, I've come up with my own 'dirty' method. I can imagine that this isn't an approved way of doing things, but there's no error thrown up and the app works as expected at all times (not often I can say that, either!):
-(IBAction)saveAsAction:(id)sender
{
NSSavePanel *saveAsPanel = [NSSavePanel savePanel];
[saveAsPanel beginSheetModalForWindow:window completionHandler:^(NSInteger userResult)
{
if (userResult == NSOKButton) {
[self saveAction:#"saveAsCalling"];
NSURL *newURL = [saveAsPanel URL];
NSError *error = nil;
[[NSFileManager defaultManager] copyItemAtURL:[NSURL fileURLWithPath:internalStore] toURL:newURL error:&error];
//internalStore is a hard-wired NSString that holds the path to the bundle's database
}
}];
}
-(IBAction)loadAction:(id)sender
{
NSOpenPanel *loadPanel = [NSOpenPanel openPanel];
[loadPanel beginSheetModalForWindow:window completionHandler:^(NSInteger userResult)
{
if (userResult == NSOKButton) {
[self saveAction:#"loadCalling"];
NSURL *newURL = [loadPanel URL];
NSURL *oldURL = [NSURL fileURLWithPath:internalStore];
NSError *error = nil;
NSPersistentStoreCoordinator *psc = [SELF_MOC persistentStoreCoordinator];
[psc removePersistentStore:[[self persistentStoreCoordinator] persistentStoreForURL:oldURL] error:&error];
[psc addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:newURL options:nil error:&error];
[[NSFileManager defaultManager] removeItemAtURL:oldURL error:&error];
[[NSFileManager defaultManager] copyItemAtURL:newURL toURL:oldURL error:&error];
[psc removePersistentStore:[[self persistentStoreCoordinator] persistentStoreForURL:newURL] error:&error];
[psc addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:oldURL options:nil error:&error];
}
}];
}
The basic reasoning is this: to do a 'SaveAs...' I simply copy out the SQLLite store file in the mainBundle to wherever the user selects and rename it to what they want - as per TechZen's suggestion.
To do a 'Load' then I first removePersistentStore from the bundle's file, add the one that the user's just chosen. Delete the bundle store (which in theory isn't now being used) and then copy the user's choice back into the bundle. Finally, the two operations of remove and addPersistentStore are performed to point the app back to it's bundle's file which is now the user's choice.
Hope that makes sense. If anyone has any thoughts on just how unprofessional a methodology this is then please - be kind as I'm fairly new - let me know. I can't find anything that is more elegant.
I know Apple don't like you using removePersistentStore and addPersistentStore but, as I say no errors are reported (in my actual code I scattered NSLog lines throughout to report what error is holding).
You only use a SaveAs... in a document based app. If you use Core Data as your model, you need to use NSPersistentDocument to save your data. It provide the SaveAs... functionality you seek.
Straight Core Data is used for more database-like apps in which the entire app operates from one data set (more or less.)

Core Data fetchedResultsController errors 'A fetch request must have an entity' entityForName returns nil

Hi I set up my own coredata app, or I tried...
First I created the xdatamodel and generated the Modelclasses, after this I implemented all the function of core-data in AppDelegate which I found in a generated project. Finally I copied the fetchedResultsController in my TableViewController.
fetchedResultsController
- (NSFetchedResultsController *)fetchedResultsController {
if (fetchedResultsController_ != nil) {
return fetchedResultsController_;
}
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"ParameterGroup" inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
[fetchRequest setFetchBatchSize:20];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"name" ascending:NO];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:nil cacheName:#"Root"];
aFetchedResultsController.delegate = self;
self.fetchedResultsController = aFetchedResultsController;
[aFetchedResultsController release];
[fetchRequest release];
[sortDescriptor release];
[sortDescriptors release];
NSError *error = nil;
if (![fetchedResultsController_ performFetch:&error]) {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
return fetchedResultsController_;
}
First I checked if the managedObjectsController is != nil, it has a address
Then I copied the EntityName from my xdatamodel in entityForName,
but NSEntityDescricption entity is nil.
And if I just create a new object the exception says, that the entity doesn't exist
Do I have to connect the xdatamodel to my project?
Hope you can help me
Thanks a lot!!!
The most common cause of this problem is simply misspelling the entity name wrong in the code such that it doesn't match the entity name in the data model.
Copy and paste the entity name from the model to the code and see if that fixes the problem.
The simplest way to solve this, given that you haven't done a lot coding on non-core-data parts, is probably to create a new project where you check the box for "Use Core Data". If you're going to use a Navigation Bar, choose this as your template. If I recall correctly, this will generate a table view with all functions needed. You'll have to modify the datamodel (generated).
Remark that you'll have to delete the app from the Simulator if it is installed and you change the datamodel (otherwise the generated data will not be consistent with the datamodel and the app will crash)

How to optimize a UILocalNotification process

I'm trying to send multiple localNofications using a fetch request on an entity
And though this code works fine
NSFetchRequest *myRequest = [[NSFetchRequest alloc] init];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"active == YES"];
[myRequest setEntity:[NSEntityDescription entityForName:#"Entry" inManagedObjectContext:managedObjectContext]];
[myRequest setPredicate:predicate];
NSError *error = nil;
NSArray *fetchedObjects = [self.managedObjectContext executeFetchRequest: myRequest error: &error];
if (fetchedObjects == nil){
// Deal with error...
}
// We fill the NSMutableArray with the values of the fetch
self.activeList = [[NSMutableArray alloc] initWithArray:[fetchedObjects valueForKey:#"textbody"]];
[self scheduleAlarms:[self.activeList objectAtIndex:0]];
[fetchedObjects release]; //this line crashes the app
1) if I release fetchedObjects, the app crashes. Aren't I supposed to release it ?
2) Could I use the localNotif.userinfo to optimize the code instead of calling a method to schedule each localNotification with the strings in my activeList ? I can't figure out how to do it.
Thanks,
Mike
1) executeFetchRequest returns an autoreleased NSArray, you don't need to release it manually
2) not clear what do you want to optimize...

Resources