Getting SIGABRT when executing fetchRequest - ios5

I'm using Xcode 4.2 and iOS SDK 5.0 and Apple's Master-Detail Application Template for iPad with Core Data. It's similar to the "Locations" sample code. I've successfully managed to pass the managed object context (MOC) from the MasterViewController (MVC) to the DetailViewController (DVC). The detail view controller accepts input from the user in some text boxes and stores that in core data; this part works like a charm. Now, I have a ActionViewController (AVC) that is a Popover View that is supposed to allow the user to e-mail all of the data in the MOC if they choose so. However, when trying to do a fetch I get a SIGABRT. I used breakpoints to pinpoint exactly where:
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
//Fetch the crosses
NSManagedObjectContext *context = [self managedObjectContext];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Event"
inManagedObjectContext:context];
[fetchRequest setEntity:entity]; // Where it crashes
//Other code
}
Also, in the debugger I see that the *context pointer is 0x0 which I'm guessing means that it's not there?
The way I made the MVC pass the MOC was like this:
// Pass the managedObjectContext to the DetailViewController as a property
_detailViewController.managedObjectContext = self.managedObjectContext;
//Pass the MOC to the actionViewController
_actionViewController.managedObjectContext = self.managedObjectContext;
I've looked at other posts and it seems that passing the MOC to all the view controllers from the AppDelegate may be the way to go but I wanted to find out why the data input works for the DVC but crashes with fetching data in the AVC. What grand error have I made?
Fixed link to github project: https://github.com/scottdaniel/fly_punnett

So I figured it out, though I'm probably breaking some "good programming" maxim...
All I did was delete
//Pass the MOC to the actionViewController
_actionViewController.managedObjectContext = self.managedObjectContext;
along with all other connections from AVC to MVC (i.e. #class ActionViewController, #property *ActionViewController in the MCV interface) << ask me for the full details if you want them or check it out on github (link above)
Then I just added
#import AppDelegate.h
to my ActionViewController.m
and modified the MOC creation like so:
-(void)tableView:(UITableView *)tableView
didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
//Fetch the crosses
NSManagedObjectContext *context = [(AppDelegate *)[[UIApplication
sharedApplication] delegate]
managedObjectContext];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription
entityForName:#"Event"
inManagedObjectContext:context];
The MOC is created successfully and I'm able take the data for the user and put it into an e-mail they can send to whoever.

Related

refresh reference ManagedObjectContext

in my iOS app i have a core data, and i have notice that sometime, in a specific view, when i retrieve information from core data, are not always up to date, i'm explain well:
if i update some value in the core data, and then i go in in a specific view to view this information, that information are not up to date, now i show how i access my database:
.h
#property (nonatomic, retain) NSManagedObjectContext *managedObjectContext;
.m
#synthesize managedObjectContext;
- (NSArray *)sortInformation{
if (managedObjectContext == nil) {
managedObjectContext = [(AppDelegate *) [[UIApplication sharedApplication] delegate] managedObjectContext];
}
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription
entityForName:#"MyEntity" inManagedObjectContext:managedObjectContext];
[fetchRequest setEntity:entity];
....
and then i display my information in a table view, all work perfectly, there is only this problem, that SOME TIME seems that the update i have done in another view is not read in this view, but if i close the app, and i close it from the background, and then i reopen it all works fine...so i have saved the update in the core data correctly, so i think the problem is in this view, maybe i have an old reference of the core data and not the update version, maybe the problem is this:
if (managedObjectContext == nil) {
managedObjectContext = [(AppDelegate *) [[UIApplication sharedApplication] delegate] managedObjectContext];
}
that refresh only if the variable managedObjectContext is nil so only if the view is deallocated...so never, because is one of my root view controller in a UITabbarController, so my question is, how i can access to the core data to have always a update version of it?
no need to refresh the context just call save method on managedObjectContext like [managedObjectContext save];
or if you are using more than one managed object context you should merge changes done by the context
On the implementation of the database class you can do like this
-(id) initWithContext: (NSManagedObjectContext *)managedObjContext {
self = [super init];
[self setManagedObjectContext:managedObjContext];
return self;
}
the managedObjContext is pass and set
On your app delegate when call the database class it should be something like this
database = [[Database alloc] initWithContext:self.managedObjectContext];
Then you are good to accessed the database like this
- (NSArray *)sortInformation {
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"MyEntity" inManagedObjectContext:managedObjectContext];
[fetchRequest setEntity:entity];
NSMutableArray *mutableFetchResults = [[[managedObjectContext_ executeFetchRequest:request error:&error] mutableCopy] autorelease];
[request release];
return mutableFetchResults;
}

How to pass ManagedObjectContext to TabBarViewControllers

After reading quite a couple of questions in stackoverflow, I still can't find an answer at the moment.
I have trouble passing ManagedObjectContext from appdelegate to my tabbarcontroller view.
in my appdelegate.m I have this
#import "memoView.h"
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
memoView *mView = (memoView *)navigationController.topViewController;
mView.ObjectContext =[self managedObjectContext];
}
in memoView.m
- (void)viewDidLoad
{
[super viewDidLoad];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription
entityForName:#"NoteLog" inManagedObjectContext:[self ObjectContext]];
[fetchRequest setEntity:entity];
NSError *error;
self.memoInfo = [ObjectContext executeFetchRequest:fetchRequest error:&error];
//self.title = #"Memo";
[fetchRequest release];
}
error reason: '+entityForName: could not locate an NSManagedObjectModel for entity name 'NoteLog''
I'm not sure what I have done wrong, I'm relative new to core data.
Any comments are appreciated.
I ran into this at one point and solved like this:
YourAppDelegate *appDelegate = (YourAppDelegate *)[[UIApplication sharedApplication] delegate];
self. ObjectContext = appDelegate.ManagedObjectContect;
Where do you put following code? I guess mView is nil when you assigning mView.ObjectContext:
#import "memoView.h"
memoView *mView = (memoView *)navigationController.topViewController;
mView.ObjectContext =[self managedObjectContext];
I'd personally implement a Singleton for my CoreData Stack. - But in a slightly safer manner like here.
The singleton pattern is basically an excuse for a "global variable". Although if implemented correctly and used wisely one of the most powerful patterns there are. Simply speaking, what it does is: It creates an object of a class IF it doesn't exist yet and stores it in a static variable of that object's class. And since a class variable's content is the same amongst all the instances of that class (obviously), the next time you try to allocate an instance of the class it checks whether it was allocated before, and if it was returns the old instance.
Thus, you can basically "allocate" from anywhere and however often you want and you'll always get the same object back.
People tend to then call that singleton class something along the lines of "DataManager" or similar.

Populating the Tableview from the Core Data

I have performed the save operation in core data and it is successfully done .It stores the data .I have also fetched the data into the log. This is my code for fetching data in log but I dont know how to fetch this data in TableView.
NSError *error;
DemoAppCoreDataAppDelegate *appdelegate = (DemoAppCoreDataAppDelegate *)[[UIApplication sharedApplication]delegate];
NSManagedObjectContext *context = [appdelegate managedObjectContext];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc]init];
NSEntityDescription *entity = [NSEntityDescription entityForName: #"Employee" inManagedObjectContext:context];
[fetchRequest setEntity:entity];
NSArray *fetchedobject = [context executeFetchRequest:fetchRequest error:&error];
tablearray = [[NSMutableArray alloc] initWithArray:fetchedobject copyItems:YES];
for (NSManagedObject *info in fetchedobject ) {
NSLog(# "%#",[info valueForKey:#"name"] );
}
[fetchRequest release];
I would recommend to use a NSFetchedResultsController.
Apple provides complete sample code in the NSFetchedResultsController documentation
The NSFetchedResultsController is specifically designed to work in between a tableView and Core Data. It makes everything a lot easier.
For example it will automatically insert and delete rows when you add or remove objects from the core data. For this you have to implement the NSFetchedResultsControllerDelegate protocol. The full sample code for this is in the protocol documentation
Use fetchedobject as dataSource to the tableView.
After [fetchRequest release]; , set the delegate and datasource for the tableView programatically.
The best way in this case is to inspect some working example I think. Look at the apple's example project Recipies, good one btw: easy and gives a good picture of how the things with CoreData and tableViews should be done:
http://developer.apple.com/library/ios/#samplecode/iPhoneCoreDataRecipes/Introduction/Intro.html#//apple_ref/doc/uid/DTS40008913

CoreData DetailTableView BAD_ACCESS Error

Maybe I'm not going about showing a detail for a selected row using CoreData, but I can't figure out why I'm getting a "BAD_ACCESS" error. I've googled around and can't find what I'm looking for.
Basically I use CoreData to populate the data for a Table View. It retrieves all of the title attributes for all of the entities. When the user clicks on a row, I have a Detail View that needs to show the description for that entity. I think I need to make a new NSManagedObjectContext and a new NSEntityDescription for a new NSFetchRequest in my DetailViewController and then use a NSPredicate to say "where title = [user selected title]". I get an error when I select a row. See code:
- (void)viewDidLoad
{
// Do any additional setup after loading the view from its nib.
// Get the objects from Core Data database
Caregiver_Activity_GuideAppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
NSManagedObjectContext *context = [appDelegate managedObjectContext];
NSEntityDescription *entityDescription = [NSEntityDescription
entityForName:#"Definition"
inManagedObjectContext:context];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:entityDescription];
NSPredicate *pred = [NSPredicate predicateWithFormat:#"(title = %#)", self.title];
[request setPredicate:pred];
NSError *error;
NSArray *objects = [context executeFetchRequest:request error:&error];
if (objects == nil) {
NSLog(#"There was an error!");
// Do whatever error handling is appropriate
}
for (NSManagedObject *oneObject in objects) {
[definitionDescriptionTextView setText:[oneObject valueForKey:#"desc"]];
}
[objects release];
[request release];
[super viewDidLoad];
}
I comment out that code and everything works. But when I try to debug with breakpoints, nothing catches. So I'm more confused.
I know CoreData is probably overkill for what I'm doing but this is a learning app for me.
EDIT: I didn't include that I'm using a sqlite database that is pre-populated with the entities.
You can also download my project on my github page.
Normally, with a Core Data backed Master-Detail interface, you don't fetch for the Detail view.
When you select a row in the Master tableview, you are selecting a particular managed object instance. You then pass that managed object instance to the detail view. There is no need to refetch the object that you selected in the tableview.
A good example of this would be the Contacts app. The Master tableview would be a list of Contact objects (displaying the name.) When you select a row, the Master tableview controller takes the specific Contact object associated with the selected row and then passes it to the Detail view controller which then populates the Detail view using data taking from the properties of the passed Contact object.
So, that entire code block where the error occurs is unnecessary.
However, the immediate error in this code is that you are releasing an object you didn't create. In this line:
NSArray *objects = [context executeFetchRequest:request error:&error];
... you are not creating a NSArray instance with a init, new or create method. Instead, you are merely receiving an autoreleased NSArray instance created and returned by the context NSManagedObjectContext instance. When you release an object you did not create here:
[objects release];
... you cause the crash.
Conversely, you do create a NSFetchRequest here:
NSFetchRequest *request = [[NSFetchRequest alloc] init];
... because you used init so you do have to balance that with:
[request relwase];
BTW, this type of code should not be put in viewDidLoad as the method is only called when the view is read in the first time from the nib file on disk. That is only guaranteed to happen once as the view may remain in memory when the user switches to another view. Instead, put code that needs to run each time the view appears in viewWillAppear.

Pass Core Data results to UILabel

I'm selecting a row from a table. I want to pass the results of that selection to a UILabel on a new View. Do I need an NSFetchRequestController subroutine for the below? I wanted a simpler way to pass the event core data selection to a non-UITableView Controller (just a regular UIViewController).
The 'request' at objectIndexPath below is causing the error.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
ReviewController *reviewViewController = [[ReviewController alloc] init];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Event" inManagedObjectContext:managedObjectContext];
[request setEntity:entity];
Event *selectedEvent = (Event *)[request objectAtIndexPath:indexPath];
reviewViewController.event = selectedEvent;
[self.navigationController pushViewController:reviewViewController animated:YES];
[reviewViewController release];
}
You don't need a new fetched results controller. You just pass the managed object associated with the selected tableview row to the next controller. You are getting the error because you haven't performed a fetch and in any case a fetch request does not have a objectAtIndexPath method.
If you have a fetched results controller for the tableview, you find the selected object with:
reviewViewController.event =[[self.fetchedResultsController fetchedObjects] objectAtIndex:index.row];