NSFetchedResultsController objectAtIndexPath crash (EXC_BAD_ACCESS) - iphone

I have a huge problem with NSFetchedResultsCOntroller. I'm using fetchedResultsContrioller and I have interface with 3 tabs. They use Core Data too. But I have a problem with only ONE of them.
Faktura *faktura = [self.fetchedResultsController objectAtIndexPath:indexPath];
cell.textLabel.text = faktura.NumerFV; // THE CRASH IS HERE
int productsCount = [faktura.Produkty count]; // OR HERE
NSString *kontrahentName = [faktura.Kontrahent valueForKey:#"NazwaKrotka"]; // OR HERE
cell.detailTextLabel.text = [NSString stringWithFormat:#"nabywca: %#, produktów: %d",kontrahentName, productsCount];
cell.imageView.image = [UIImage imageNamed:#"faktura_cell_image.png"];
cell.hidesImageWhileEditing = YES;
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
Faktura is my NSManagedObject subclass.
NSZombie says:
-[CFDictionary retain]: message sent to deallocated instance 0x5d619d0
My fetchedResultsController implementation:
- (NSFetchedResultsController *)fetchedResultsController {
if (__fetchedResultsController != nil) return __fetchedResultsController;
// Setup the table
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Faktura" inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
// Setup the sort descriptors
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"DataWystawienia" ascending:YES];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
// Create the fetched results controller
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:nil cacheName:nil];
aFetchedResultsController.delegate = self;
self.fetchedResultsController = aFetchedResultsController;
[aFetchedResultsController release];
[fetchRequest release];
[sortDescriptor release];
[sortDescriptors release];
NSError *error = nil;
if (![self.fetchedResultsController performFetch:&error]) { // THE CRASH IS HERE UNLESS I INIT NSFetchedResultsController WITH cacheName:nil. (IF IT'LL BE cacheName:#"Root" IT CRASHES.)
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Błąd Krytyczny" message:#"Wystąpił nieznany błąd przy zmienianiu zawartości w bazie danych. Dla dobra twoich danych prosimy niezwłocznie wyjść z aplikacji i spróbować ponownie." delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alert show];
[alert release];
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
return __fetchedResultsController;
}
Faktura.h
#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>
#interface NSArrayToDataTransformer : NSValueTransformer #end
#interface NSDictionaryToDataTransformer : NSValueTransformer #end
#interface Faktura : NSManagedObject
#property (nonatomic, retain) NSDate * DataZaplaty;
#property (nonatomic, retain) NSString * NumerFV;
#property (nonatomic, retain) NSDate * DataWystawienia;
#property (nonatomic, retain) NSDate * DataSprzedazy;
#property (nonatomic, retain) id Produkty;
#property (nonatomic, retain) id Kontrahent;
#end
Faktura.m
#import "Faktura.h"
#implementation Faktura
#dynamic DataZaplaty;
#dynamic NumerFV;
#dynamic DataWystawienia;
#dynamic DataSprzedazy;
#dynamic Produkty;
#dynamic Kontrahent;
+ (void)initialize {
if (self == [Faktura class] ) {
NSArrayToDataTransformer *arrayTransformer = [[NSArrayToDataTransformer alloc] init];
[NSValueTransformer setValueTransformer:arrayTransformer forName:#"NSArrayToDataTransformer"];
NSDictionaryToDataTransformer *dictTransformer = [[NSDictionaryToDataTransformer alloc] init];
[NSValueTransformer setValueTransformer:dictTransformer forName:#"NSDictionaryToDataTransformer"];
}
}
#end
#implementation NSArrayToDataTransformer
+ (BOOL)allowsReverseTransformation {
return YES;
}
+ (Class)transformedValueClass {
return [NSData class];
}
- (id)transformedValue:(id)value {
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:value];
return data;
}
- (id)reverseTransformedValue:(id)value {
NSArray *array = [NSKeyedUnarchiver unarchiveObjectWithData:value];
return [array autorelease];
}
#end
#implementation NSDictionaryToDataTransformer
+ (BOOL)allowsReverseTransformation {
return YES;
}
+ (Class)transformedValueClass {
return [NSData class];
}
- (id)transformedValue:(id)value {
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:value];
return data;
}
- (id)reverseTransformedValue:(id)value {
NSDictionary *dict = [NSKeyedUnarchiver unarchiveObjectWithData:value];
return [dict autorelease];
}
#end
My Faktura object insertion code
- (void)fakturaCreator:(FakturaCreator *)form didEndWithValues:(NSDictionary *)values {
NSManagedObjectContext *context = [self.fetchedResultsController managedObjectContext];
NSEntityDescription *entity = [[self.fetchedResultsController fetchRequest] entity];
Faktura *newFaktura = [NSEntityDescription insertNewObjectForEntityForName:[entity name] inManagedObjectContext:context];
[newFaktura setNumerFV:[values valueForKey:#"id"]];
[newFaktura setDataWystawienia:[values valueForKey:#"creationDate"]];
[newFaktura setDataSprzedazy:[values valueForKey:#"sellDate"]];
[newFaktura setDataZaplaty:[values valueForKey:#"paymentDate"]];
[newFaktura setKontrahent:[values valueForKey:#"kontrahent"]];
[newFaktura setProdukty:[values valueForKey:#"produkty"]];
NSError *error = nil;
if (![context save:&error]) {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
[self.emptySectionView setHidden:YES];
}
FakturaCreator is my UIViewController where user create an invoice. values array contains dictionary of values: my invoice number (NSString), various dates (NSDate), client (NSDictionary) and products (NSArray).
Please help me!
If you want some additional code, i'll put it here.
EDIT: It is definetly objectAtIndexPath: fault. When I comment all the cell setup code (it will display empty cell) and then try to remove the cell, app crashes on line which contains objectatIndexPath:.
EDIT #2: Anybody? Please, help me... :(

I think I see the problem. In your NSArrayToDataTransformer, you have this:
- (id)reverseTransformedValue:(id)value {
NSArray *array = [NSKeyedUnarchiver unarchiveObjectWithData:value];
return [array autorelease];
}
Since unarchiveObjectWithData: does not begin with "alloc", "new", "copy", or "mutableCopy", you do not own the object and therefore may not autorelease it. You have the same problem in your NSDictionaryToDataTransformer.

Your inclusion of the value transformers into the class and the using +initialize is non-standard. Although +initialize should work, it is recommended in the Core Data docs not use any kind of initialization methods at all but to rely on awakeFromFetch for initialization.
I would check your tableview methods such as numberOfRowsInSection: to ensure you are getting the right indexes back from the tableview. If the rows in the tableview and the count of the fetchedObjects array come out of sync, you can get this kind of crash.

Related

Core data sort data by two Descriptor

I have used core data in my ios app. Now i have one table in which there are two columns .
Category
Order (In which there are NSNumber 1, 2 ,5)
I want to fetch data so It will first sort alphabetically by Category name and than by Order Number.
I have used below code :
NSEntityDescription *entity_Maintness = [NSEntityDescription
entityForName:#"Maintness" inManagedObjectContext:__managedObjectContext];
[fetchRequest_Maintness setEntity:entity_Maintness];
NSSortDescriptor *sortDescriptor1 = [[NSSortDescriptor alloc] initWithKey:#"type" ascending:YES];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"serialNumber" ascending:NO];
NSArray *sortDescriptors12 = [[NSArray alloc] initWithObjects:sortDescriptor1, sortDescriptor, nil];
[fetchRequest_Maintness setSortDescriptors:sortDescriptors12];
But the data are sorted only by Category not by Serial Number.
Thanks for Help.
The code above looks ok. Maybe there is an issue elsewhere in your code? Here is some code I quickly generated for you that may serve as starting point??
#import "MainViewController.h"
#import "Maintness.h"
#interface MainViewController ()
#property (weak, nonatomic) IBOutlet UITextField *type;
#property (weak, nonatomic) IBOutlet UITextField *serialNumber;
#property (strong, nonatomic) NSManagedObjectContext *context;
- (IBAction)addButtonPressed:(id)sender;
- (IBAction)retrieveButtonPressed:(id)sender;
#end
#implementation MainViewController
#pragma mark - lazy instantiation
- (NSManagedObjectContext *)context{
if (!_context){
id appDelegate = (id)[[UIApplication sharedApplication] delegate];
_context = [appDelegate managedObjectContext];
}
return _context;
}
#pragma mark - core data interactions
- (IBAction)addButtonPressed:(id)sender {
NSError *error = nil;
Maintness *newMaintness = nil;
newMaintness = [NSEntityDescription insertNewObjectForEntityForName:#"Maintness" inManagedObjectContext:self.context];
newMaintness.type = self.type.text;
newMaintness.serialNumber = self.serialNumber.text;
if (![self.context save:&error]) {
NSLog(#"Oh no - error: %#", [error localizedDescription]);
} else {
NSLog(#"It appears the details were added ok");
}
}
- (IBAction)retrieveButtonPressed:(id)sender {
NSError *error = nil;
// Set up fetch
NSFetchRequest *fetch = [[NSFetchRequest alloc] init];
// Set up entity
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Maintness" inManagedObjectContext:self.context];
[fetch setEntity:entity];
// Set up sorting
// - sorts in order of array
NSSortDescriptor *primarySortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"type" ascending:YES];
NSSortDescriptor *secondarySortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"serialNumber" ascending:NO];
NSArray *sortDescriptors = #[primarySortDescriptor, secondarySortDescriptor];
[fetch setSortDescriptors:sortDescriptors];
NSArray *results = [self.context executeFetchRequest:fetch error:&error];
for (Maintness *result in results) {
NSLog(#"type: %# serial number: %#", result.type, result.serialNumber);
}
}
#end

Completion block/callback on FetchRequest completion

I have a FetchRequest in my model class named ContentManager. It fetches quite a lot of data, so the screen is blank until it's completion. In the ViewController that displays the fetched results, I would like to show a loading indicator, so I would like to get a callback when the FetchRequest has completed and pass that to the ViewController to stop the loading indicator. Is this possible?
Here is the FetchRequest from the ContentManager class:
- (NSArray*)recipesForMagazine
{
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Recipe" inManagedObjectContext:managedObjectContext];
[request setEntity:entity];
NSString* path = [[NSBundle mainBundle] pathForResource:#"magazine_recipe_guids" ofType:#"plist"];
NSArray* recipeGuids = [NSArray arrayWithContentsOfFile:path];
NSPredicate* predicate = [NSPredicate predicateWithFormat:#"guid IN %#",recipeGuids];
[request setPredicate:predicate];
NSSortDescriptor* sort = [[NSSortDescriptor alloc] initWithKey:#"title" ascending:YES selector:#selector(localizedCaseInsensitiveCompare:)];
[request setSortDescriptors:[NSArray arrayWithObject:sort]];
[sort release];
NSError* error = nil;
NSArray* fetchResults = [managedObjectContext executeFetchRequest:request error:&error];
[request release];
return fetchResults;
}
Here I set it up in the ViewController
self.magazineRecipes = [[ContentManager defaultManager] recipesForMagazine];
I want to set up the fetchrequest method like this:
- (NSArray*)recipesForMagazine:(void (^)(BOOL success))block or something, so in the viewcontroller I can call it like this
self.magazineRecipes = [[CTContentManager defaultManager] recipesForMagazine:^(BOOL success) {
if (success) {
//stop activity indicator view
}
}];
I don't know if I'm at all in the right way of thinking, thanks for your help in advance!
I would make the viewController a delegate of the ContentManager class. So in the ContentManager.h I would do something like:
#protocol ContentManagerDelegate()
-(void) didFetchResults:(NSArray *) results;
-(void) didResultsFail: (NSError *) error;
#end
#interface ContentManager : <SuperClass Name>
-(id) initWithDelegate: (id<ContentManagerDelegate>) delegate;
#property (nonatomic, strong) id<ContentManagerDelegate> delegate;
#end
and in the implementation:
-(id) initWithDelegate: (id<ContentManagerDelegate>) delegate
{
self = [super init];
if(self)
{
_delegate = delegate;
}
return self;
}
and in your recipesForMagazine method you can use the delegate [_delegate didFetchResults: fetchResults], you can implement a method to pass errors to the delegate if you want as well. In your ViewController.h do #interface ViewController.h : UIViewController <ContentManagerDelegate> and in the implementation you should be able to implement the didFetchResults: method that will give you the results and in that method you can stop the activity indicator from animating.

Pulling certain key/value pairs from core data fetched results

:D Apologies I am brand new to this and I am trying to wrap my head around core data. I have two view controllers. the first one (called PVC) is a table view controller with two sections that holds names and emails that a user enters. They press add from that screen and it takes them to the second view controller (called PDVC) where the user adds the name and email and specifies which section the entry will fall under (trustee or recipient). When they hit done on that screen, the info gets saved into core data and it takes them back to the PVC where core data fetches that info and puts it into the table view.
What I'm trying to accomplish is when the user taps send from the PVC, I need to take that info, (each name and email entered) JSON it, and send it off to a server. I'm doing this successfully with a hack I conjured up that stores each item into the dictionary, and then takes those key/value pairs and adds them to a mutable array all from the PDVC. The problem is, all of this data needs to be as persistent as core data so it makes sense to use core data for this info instead of my hack. My question is, is that possible? I need to be able to 'extract' certain key value pairs from core data. If I can do that, I'm sure I can figure out the rest. I understand that theres a method you can use to store core data info into an array but it also comes along with other info that I can't use:
array of fetched objects: (
"<Credentials: 0xb9c6ed0> (entity: Credentials; id: 0xb9bfa90 <x-coredata://BE51A026-9D1B- 4A93-BA34-4EAC55B8B9DF/Credentials/p1> ; data: {\n category = Trustees;\n name = aef;\n settingsEmail = \"inawef#hioe.com\";\n})",
"<Credentials: 0xb9c6f40> (entity: Credentials; id: 0xb9c6ae0 <x-coredata://BE51A026-9D1B- 4A93-BA34-4EAC55B8B9DF/Credentials/p3> ; data: {\n category = Trustees;\n name = awef;\n settingsEmail = \"waef#aioc.com\";\n})",
"<Credentials: 0xb9c6fc0> (entity: Credentials; id: 0xb9c6af0 <x-coredata://BE51A026-9D1B- 4A93-BA34-4EAC55B8B9DF/Credentials/p2> ;
I just need from that info the name and settingsEmail key/value pairs. Any help would be greatly appreciated! Here's the rest of my code
PVC.h
#interface PeopleViewController : UITableViewController<NSFetchedResultsControllerDelegate>
{
NSMutableArray *listOfItems;
}
- (IBAction)doneButtonPressed:(id)sender;
- (IBAction)sendPressed:(id)sender;
#property (nonatomic, strong) NSManagedObjectContext *managedObjectContext;
#end
PVC.m
#import "PeopleViewController.h"
#import "AppDelegate.h"
#import "PeopleDetailViewController.h"
#import "Credentials.h"
#import "AlertLoad.h"
#import "Data.h"
#import "ASIFormDataRequest.h"
#interface PeopleViewController ()
#end
#implementation PeopleViewController
{
NSFetchedResultsController *fetchedResultsController;
AlertLoad *alertLoad;
Data *dataObj;
}
#synthesize managedObjectContext;
- (NSFetchedResultsController *)fetchedResultsController
{
if (fetchedResultsController == nil)
{
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Credentials" inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
NSSortDescriptor *sortDescriptor1 = [NSSortDescriptor sortDescriptorWithKey:#"category" ascending:NO];
NSSortDescriptor *sortDescriptor2 = [NSSortDescriptor sortDescriptorWithKey:#"name" ascending:YES];
[fetchRequest setSortDescriptors:[NSArray arrayWithObjects:sortDescriptor1, sortDescriptor2, nil]];
[fetchRequest setFetchBatchSize:20];
fetchedResultsController = [[NSFetchedResultsController alloc]
initWithFetchRequest:fetchRequest
managedObjectContext:self.managedObjectContext
sectionNameKeyPath:#"category"
cacheName:#"Credentials"];
fetchedResultsController.delegate = self;
}
return fetchedResultsController;
}
- (void)performFetch
{
NSError *error;
if (![self.fetchedResultsController performFetch:&error]) {
FATAL_CORE_DATA_ERROR(error);
return;
}
}
- (void)viewDidLoad
{
[super viewDidLoad];
NSLog(#"view did load");
dataObj = [Data dataObj];
dataObj.trusteeArray = [[NSMutableArray alloc]init];
dataObj.recipientArray = [[NSMutableArray alloc]init];
if (self.managedObjectContext == nil)
{
self.managedObjectContext = [(AppDelegate *) [[UIApplication sharedApplication] delegate] managedObjectContext];
NSLog(#"After managedObjectContext: %#", self.managedObjectContext);
}
[self performFetch];
NSLog(#"array of fetched objects: %#", [fetchedResultsController fetchedObjects]);
[self.navigationController setToolbarHidden:NO];
self.navigationItem.title = #"Swipe to delete";
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning]; // Releases the view if it doesn't have a superview
// Release anything that's not essential, such as cached data
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
PeopleDetailViewController *controller = segue.destinationViewController;
controller.managedObjectContext = self.managedObjectContext;
}
#pragma mark Table view methods
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
//return [listOfItems count];
return [[self.fetchedResultsController sections] count];
}
// Customize the number of rows in the table view.
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
id <NSFetchedResultsSectionInfo> sectionInfo = [[self.fetchedResultsController sections] objectAtIndex:section];
return [sectionInfo numberOfObjects];
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection: (NSInteger)section
{
id <NSFetchedResultsSectionInfo> sectionInfo = [[self.fetchedResultsController sections] objectAtIndex:section];
return [sectionInfo name];
}
- (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath
{
Credentials *credentials = [self.fetchedResultsController objectAtIndexPath:indexPath];
if ([credentials.name length] > 0)
{
cell.textLabel.text = credentials.name;
}
else
{
cell.textLabel.text = #"(New Entry)";
}
if (credentials.settingsEmail != nil)
{
cell.detailTextLabel.text = credentials.settingsEmail;
}
else
{
cell.detailTextLabel.text = #"Tap to edit";
}
}
- (void)tableView:(UITableView *)tableView commitEditingStyle: (UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
if (editingStyle == UITableViewCellEditingStyleDelete) {
Credentials *credentials = [self.fetchedResultsController objectAtIndexPath:indexPath];
[self.managedObjectContext deleteObject:credentials];
NSError *error;
if (![self.managedObjectContext save:&error]) {
FATAL_CORE_DATA_ERROR(error);
return;
}
}
}
// Customize the appearance of table view cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle
reuseIdentifier:CellIdentifier];
}
[self configureCell:cell atIndexPath:indexPath];
// Set up the cell...
return cell;
}
-(void)infoSent
{
[alertLoad close];
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Success" message:#"Your trustees and recipients have been submitted" delegate:nil cancelButtonTitle:#"OK" otherButtonTitles: nil];
[alert show];
[self dismissViewControllerAnimated:YES completion:nil];
}
-(void)sendDataWithString:(NSString *)string
{
NSURL *url = [NSURL URLWithString:#"http://lastwords.com.au/lastwords/"];
ASIFormDataRequest *request = [ASIFormDataRequest requestWithURL:url];
[request setPostValue:dataObj.username forKey:#"author"];
[request setPostValue:dataObj.password forKey:#"password"];
[request setPostValue:string forKey:#"participants"];
[request setPostValue:#"set_participants" forKey:#"json"];
[request startSynchronous];
NSError *error = [request error];
if (error != nil)
{
[alertLoad close];
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Error" message:error.localizedDescription delegate:nil cancelButtonTitle:#"OK" otherButtonTitles: nil];
[alert show];
NSLog(#"error: %#", error);
}
else
{
NSString *response = [request responseString];
NSLog(#"response: %#", response);
[self performSelectorOnMainThread:#selector(infoSent) withObject:nil waitUntilDone:YES];
}
}
- (IBAction)doneButtonPressed:(id)sender
{
[self.navigationController dismissViewControllerAnimated:YES completion:nil];
}
- (IBAction)sendPressed:(id)sender
{
if ([self.tableView numberOfRowsInSection:0] > 3)
{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Too many trustees" message:#"You can only submit a maximum of 3 trustees." delegate:nil cancelButtonTitle:#"OK" otherButtonTitles: nil];
[alert show];
}
else if ([self.tableView numberOfRowsInSection:0] < 3)
{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Not enough trustees" message:#"You must select 3 trustees." delegate:nil cancelButtonTitle:#"OK" otherButtonTitles: nil];
[alert show];
}
else if ([self.tableView numberOfSections] < 2)
{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"No trustees or recipients" message:#"You must enter at least enter 3 trustees and at least one recipient." delegate:nil cancelButtonTitle:#"OK" otherButtonTitles: nil];
[alert show];
}
else
{
alertLoad = [[AlertLoad alloc]
initWithTitle:#"Submitting..."
message:#"Submitting your trustees and recipients. Please wait...\n\n\n"
delegate:self cancelButtonTitle:#"Cancel"
otherButtonTitles:nil];
alertLoad.delegate = self;
[alertLoad show];
NSDictionary *dictionary = [NSDictionary dictionaryWithObjectsAndKeys:dataObj.trusteeArray,#"trustees", dataObj.recipientArray, #"recipients", nil];
NSError *error;
NSData* jsonData = [NSJSONSerialization dataWithJSONObject:dictionary options:kNilOptions error:&error];
NSString* jSONString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
NSLog(#"json??: %#", jSONString);
[self performSelectorInBackground:#selector(sendDataWithString:) withObject:jSONString];
}
}
#end
(took out fetch results delegate stuff to save space)
PDVC.h
#interface PeopleDetailViewController : UIViewController<UITextFieldDelegate>
#property (weak, nonatomic) IBOutlet UITextField *txtFieldName;
#property (weak, nonatomic) IBOutlet UITextField *txtFieldEmail;
#property (nonatomic, strong) NSManagedObjectContext *managedObjectContext;
#property (weak, nonatomic) IBOutlet UISegmentedControl *segment;
- (IBAction)done:(id)sender;
#end
PDVC.m
#import "PeopleDetailViewController.h"
#import "AppDelegate.h"
#import "Credentials.h"
#import "Data.h"
#interface PeopleDetailViewController ()
{
Data *dataObj;
}
#end
#implementation PeopleDetailViewController
#synthesize managedObjectContext;
- (void)viewDidLoad
{
[super viewDidLoad];
NSLog(#"view did load");
dataObj = [Data dataObj];
if (self.managedObjectContext == nil)
{
self.managedObjectContext = [(AppDelegate *) [[UIApplication sharedApplication] delegate] managedObjectContext];
NSLog(#"After managedObjectContext: %#", self.managedObjectContext);
}
// Uncomment the following line to preserve selection between presentations.
// self.clearsSelectionOnViewWillAppear = NO;
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem;
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#pragma mark - textField Delegate
- (BOOL)textFieldShouldReturn:(UITextField *)textField
{
[textField resignFirstResponder];
return YES;
}
- (IBAction)done:(id)sender
{
NSString *emailRegEx = #"[A-Z0-9a-z._%+-]+#[A-Za-z0-9.-]+\\.[A-Za-z]{2,4}";
NSPredicate *emailTest = [NSPredicate predicateWithFormat:#"SELF MATCHES %#", emailRegEx];
if (self.txtFieldEmail.text.length < 1 || self.txtFieldName.text.length < 1)
{
[self dismissViewControllerAnimated:YES completion:nil];
}
else if ([emailTest evaluateWithObject:self.txtFieldEmail.text] == NO)
{
//Valid email addres
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Invalid email" message:#"Please enter a valid email address" delegate:nil cancelButtonTitle:#"OK" otherButtonTitles: nil];
[alert show];
}
else
{
Credentials *credentials = [NSEntityDescription insertNewObjectForEntityForName:#"Credentials" inManagedObjectContext:self.managedObjectContext];
credentials.name = self.txtFieldName.text;
credentials.settingsEmail = self.txtFieldEmail.text;
if (self.segment.selectedSegmentIndex == 0)
{
credentials.category = #"Trustees";
NSDictionary *trusteeDict = [NSDictionary dictionaryWithObjectsAndKeys:self.txtFieldName.text,#"name", self.txtFieldEmail.text,#"email", nil];
NSError *error;
NSData* jsonData = [NSJSONSerialization dataWithJSONObject:trusteeDict options:kNilOptions error:&error];
NSString* jSONString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
NSLog(#"json??: %#", jSONString);
[dataObj.trusteeArray addObject:trusteeDict];
NSLog(#"trusteeArray: %#", dataObj.trusteeArray);
}
else
{
credentials.category = #"Recipients";
NSDictionary *recipientDict = [NSDictionary dictionaryWithObjectsAndKeys:self.txtFieldName.text,#"name", self.txtFieldEmail.text,#"email", nil];
NSError *error;
NSData* jsonData = [NSJSONSerialization dataWithJSONObject:recipientDict options:kNilOptions error:&error];
NSString* jSONString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
NSLog(#"json??: %#", jSONString);
[dataObj.recipientArray addObject:recipientDict];
NSLog(#"recipientArray: %#", dataObj.recipientArray);
}
NSError *error;
if (![self.managedObjectContext save:&error])
{
FATAL_CORE_DATA_ERROR(error);
return;
}
[self dismissViewControllerAnimated:YES completion:nil];
}
}
#end
Your question is a bit confusing but you can fetch specific information from core data with 2 very simple approaches.
1) use a predicate in the fetch like this
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"User"
inManagedObjectContext:self.managedObjectContext];
fetchRequest.entity = entity;
fetchRequest.predicate = [NSPredicate predicateWithFormat:#"userID = %#", userIDYouAreLookingfor];
NSArray *fetchedObjects = [self.managedObjectContext executeFetchRequest:fetchRequest error:&error];
NSLog(#"%#",fetchedObjects);
2) A parent relation has its entities specified in a NSSet, you can test them to retrieve certain objects:
NSSet *picturesResult = [_thumbnailsStorage.pictures objectsPassingTest:^BOOL(id obj, BOOL *stop) {
Picture *pic = obj;
// Check for a condition here
if (someCondition) {
return YES;
}
return NO;
}];
If this is not what you asked let me know and ill see if I can help.

Change NSFetchedResultsController on SegmentedControl change

I've got a UITableView which is filled using a Core Data NSFetchedResultsController. I've now added a UISegmentedControl to the view and when you change the current segment, I'd like the contents of the tableview to change.
I've read around online that it would be wise to use two different NSFetchedResultsControllers as then I can benefit from the built-in caching. Only problem is I can't seem to find any sample code for doing this and don't know where to begin.
Can anyone explain where to begin in creating a second NSFetchedResultsController and changing which sources the tableview based on the segmented control?
View header code:
#import <CoreData/CoreData.h>
#interface DomainViewController : UIViewController <NSFetchedResultsControllerDelegate, UITableViewDataSource, UITableViewDelegate> {
UITableView *domainView;
UISegmentedControl *segmentedControl;
NSString *domain;
}
#property (nonatomic, retain) NSFetchedResultsController *fetchedResultsController;
#property (nonatomic, retain) NSManagedObjectContext *managedObjectContext;
#property (nonatomic, retain) NSString *domain;
#property (nonatomic, retain) IBOutlet UITableView *domainView;
#property (nonatomic, retain) IBOutlet UISegmentedControl *segmentedControl;
- (IBAction)segmentedControlIndexChanged;
View implementation code:
#interface DomainViewController ()
- (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath;
#end
#implementation DomainViewController
#synthesize fetchedResultsController = __fetchedResultsController;
#synthesize managedObjectContext = __managedObjectContext;
#synthesize domain;
#synthesize domainView;
#synthesize segmentedControl;
- (void)viewDidLoad
{
[super viewDidLoad];
if (self.managedObjectContext == nil)
{
self.managedObjectContext = [(GARankingsAppDelegate *)[[UIApplication sharedApplication] delegate] managedObjectContext];
}
}
- (IBAction)segmentedControlIndexChanged
{
switch(self.segmentedControl.selectedSegmentIndex){
case 0:
break;
case 1:
break;
default:
break;
}
}
- (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath
{
NSManagedObject *managedObject = [self.fetchedResultsController objectAtIndexPath:indexPath];
cell.textLabel.text = [[managedObject valueForKey:#"Keyphrase"] description];
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return [[self.fetchedResultsController sections] count];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
id <NSFetchedResultsSectionInfo> sectionInfo = [[self.fetchedResultsController sections] objectAtIndex:section];
return [sectionInfo numberOfObjects];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
// Configure the cell.
[self configureCell:cell atIndexPath:indexPath];
return cell;
}
- (NSFetchedResultsController *)fetchedResultsController
{
if (__fetchedResultsController != nil)
{
return __fetchedResultsController;
}
/*
Set up the fetched results controller.
*/
// Create the fetch request for the entity.
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
// Edit the entity name as appropriate.
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Result" inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
// Set the batch size to a suitable number.
[fetchRequest setFetchBatchSize:20];
// Edit the sort key as appropriate.
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"Keyphrase" ascending:NO];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
// Edit the section name key path and cache name if appropriate.
// nil for section name key path means "no sections".
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 (![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. If it is not possible to recover from the error, display an alert panel that instructs the user to quit the application by pressing the Home button.
*/
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
return __fetchedResultsController;
}
Any help is much appreciated. Thanks.
UPDATE: updated code
View header code:
#property (nonatomic, retain) NSFetchedResultsController *currentFetchedResultsController;
#property (nonatomic, retain) NSFetchedResultsController *competitorFetchedResultsController;
#property (nonatomic, retain) NSFetchedResultsController *keyphraseFetchedResultsController;
View implementation code:
#synthesize currentFetchedResultsController = __fetchedResultsController;
#synthesize competitorFetchedResultsController;
#synthesize keyphraseFetchedResultsController;
- (NSFetchedResultsController *)fetchedResultsController
{
if (__fetchedResultsController != nil)
{
return __fetchedResultsController;
}
/*
Set up the fetched results controller.
*/
// Create the fetch request for the entity.
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
// Edit the entity name as appropriate.
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Result" inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
// Set the batch size to a suitable number.
[fetchRequest setFetchBatchSize:20];
// Edit the sort key as appropriate.
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"Keyphrase" ascending:NO];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
// Edit the section name key path and cache name if appropriate.
// nil for section name key path means "no sections".
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:nil cacheName:#"Root"];
aFetchedResultsController.delegate = self;
//self.fetchedResultsController = aFetchedResultsController;
self.currentFetchedResultsController = aFetchedResultsController;
[aFetchedResultsController release];
[fetchRequest release];
[sortDescriptor release];
[sortDescriptors release];
NSError *error = nil;
//if (![self.fetchedResultsController performFetch:&error])
if (![self.currentFetchedResultsController 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. If it is not possible to recover from the error, display an alert panel that instructs the user to quit the application by pressing the Home button.
*/
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
return __fetchedResultsController;
}
You could add another instance variable that represents the currently selected NSFetchedResultsController. And when the UISegmentedControl changes update this ivar.
this could be the action that is triggered by the valuechange event of the segment
- (IBAction *)segmentChanged:(UISegmentedControl *)sender {
if ([sender selectedSegmentIndex] == 0) {
self.currentFetchedResultsController = self.nsfrc1;
[self.tableView reloadData];
}
else if ([sender selectedSegmentIndex] == 1) {
self.currentFetchedResultsController = self.nsfrc2;
[self.tableView reloadData];
}
}
one UITableViewDataSource method as example:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return [[self.currentFetchedResultsController sections] count];
}
and you have to make sure that only the current nsfrc triggers a tableview update in the NSFetchedResultsControllerDelegate methods. So you have to change all of them too.
- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller {
if (controller == self.currentFetchedResultsController) {
[self.tableView beginUpdates];
}
}
EDIT: Nope, you are doing it wrong. the currentFetchedResultsController is just an ivar, without a lazy loading getter. It's "just" a pointer to the currently used controller.
But the two other fetchedResultsControllers should have such a lazy loading getter.
- (NSFetchedResultsController *)competitorFetchedResultsController {
if (!myCompetitorFetchedResultsController) {
// create competitorFetchedResultsController
}
return myCompetitorFetchedResultsController;
}
- (NSFetchedResultsController *)keyphraseFetchedResultsController {
if (!myKeyphraseFetchedResultsController) {
// create keyphraseFetchedResultsController
}
return myKeyphraseFetchedResultsController;
}
and then switch with:
self.currentFetchedResultsController = self.keyphraseFetchedResultsController;
or
self.currentFetchedResultsController = self.competitorFetchedResultsController;

Problem with entityForName & ManagedObjectContext when extending tutorial material

Afternoon all,
I tried to add a second data entity to the persistent store in the (locations) coredata tutorial code, and then access this in a new view. I think that I've followed the tutorial, and checked that I'm doing a clean build etc, but can't see what to change to prevent it crashing.
I'm afraid I'm at my wits end with this one, and can't seem to find the step that I've missed. I've pasted the header and code files below, please let me know if I need to share any more of the code. The crash seems to happen on the line:
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Album" inManagedObjectContext:[self managedObjectContext]];
There is one other line in the code that refers to galleryviewcontroller at the moment, and that's in the main application delegate:
galleryViewController.managedObjectContext = [self managedObjectContext];
GalleryViewController.h
#import <UIKit/UIKit.h>
#interface GalleryViewController : UIViewController {
NSManagedObjectContext *managedObjectContext;
int rowNumber;
IBOutlet UILabel *lblMessage;
UIBarButtonItem *addButton;
NSMutableArray *imagesArray;
}
#property (readwrite) int rowNumber;
#property (nonatomic,retain) UILabel *lblMessage;
#property (nonatomic,retain) NSMutableArray *imagesArray;
#property (nonatomic, retain) NSManagedObjectContext *managedObjectContext;
#property (nonatomic, retain) UIBarButtonItem *addButton;
-(void)updateRowNumber:(int)theIndex;
-(void)addImage;
#end
GalleryViewController.m
#import "RootViewController.h"
#import "LocationsAppDelegate.h"
#import "Album.h"
#import "GalleryViewController.h"
#import "Image.h"
#implementation GalleryViewController
#synthesize lblMessage,rowNumber,addButton,managedObjectContext;
#synthesize imagesArray;
/*
// The designated initializer. Override if you create the controller programmatically and want to perform customization that is not appropriate for viewDidLoad.
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
if ((self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil])) {
// Custom initialization
}
return self;
}
*/
-(void)updateRowNumber:(int)theIndex{
rowNumber=theIndex;
LocationsAppDelegate *mainDelegate =(LocationsAppDelegate *)[[UIApplication sharedApplication] delegate];
Album *anAlbum = [mainDelegate.albumsArray objectAtIndex:rowNumber];
lblMessage.text = anAlbum.uniqueAlbumIdentifier;
}
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
[super viewDidLoad];
addButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:#selector(addImage)];
addButton.enabled = YES;
self.navigationItem.rightBarButtonItem = addButton;
/* Found this in another answer, adding it to the code didn't help.
if (managedObjectContext == nil) {
managedObjectContext = [[[UIApplication sharedApplication] delegate] managedObjectContext];
}
*/
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Album" inManagedObjectContext:[self managedObjectContext]];
[request setEntity:entity];
// Order the albums by creation date, most recent first.
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"imagePath" ascending:NO];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
[request setSortDescriptors:sortDescriptors];
[sortDescriptor release];
[sortDescriptors release];
// Execute the fetch -- create a mutable copy of the result.
NSError *error = nil;
NSMutableArray *mutableFetchResults = [[managedObjectContext executeFetchRequest:request error:&error] mutableCopy];
if (mutableFetchResults == nil) {
// Handle the error.
}
[self setImagesArray:mutableFetchResults];
int a = 5;
int b = 10;
for( int i=0; i<[imagesArray count]; i++ )
{
if( a == 325 )
{
a = 5;
b += 70;
}
UIImageView *any = [[UIImageView alloc] initWithFrame:CGRectMake(a,b,70,60)];
any.image = [imagesArray objectAtIndex:i];
any.tag = i;
[self.view addSubview:any];
[any release];
a += 80;
}
}
-(void)addImage{
NSString *msg = [NSString stringWithFormat:#"%i",rowNumber];
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Add image to" message:msg
delegate:self cancelButtonTitle:#"No" otherButtonTitles:#"Yes", nil];
[alert show];
[alert release];
}
- (void)didReceiveMemoryWarning {
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
- (void)viewDidUnload {
[super viewDidUnload];
}
- (void)dealloc {
[lblMessage release];
[managedObjectContext release];
[super dealloc];
}
#end
It helps to post the error you are getting.
Have you tried the following:
Clean build of your project
Resetting the simulator to remove the older sqlite data file
If neither of those solve the issue then post the error you are getting as an update to your question.
update
The information I was looking for would be printing out in the console, while the debugger is an infinitely useful tool and I recommend learning it, in this case your issue would have been resolved by reviewing the console output of your application.