amitsbajaj
amitsbajaj

Reputation: 1304

Passing a Core Data Object to a new Table View Controller, but Using the Object's Relationship to Display That Data in the new View Controller

I'm facing an uncaught exception with the wrong selector sent during runtime, of a core-data based app when passing objects between table view controllers.

The problem I'm facing (which will become clearer as you read through this question) is I'm not sure how to reference a Core Data Objects' relationship rather than calling the Object itself.

Let's take a step back to look at the Model and the premise of the app is simple; allow users to create "Transactions" which has a name, amount, occasion and date.

Transaction Entity with wasGiven attribute Person Entity with name attribute Occasion Entity with title attribute Item Entity with amount attribute The transaction has a relationship to the Person entity (whoBy), a link to the Occasion Entity (occasion) and a link to the Item Entity (itemType).

EDIT: Core Data Model

enter image description here

The App is a simple 2 Tabb-ed Table View Controller - the first tab contains all of the information, sectioned off by dates. For this TableViewController, I am looking into the "Transaction Entity" with the following fetchedResultsController code:

- (NSFetchedResultsController *)fetchedResultsController
{
    NSManagedObjectContext *managedObjectContext = [self managedObjectContext];

    if (_fetchedResultsController != nil)
    {
        return _fetchedResultsController;
    }

    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];    
    NSEntityDescription *entity = [NSEntityDescription entityForName:@"Transaction" inManagedObjectContext:managedObjectContext];
    fetchRequest.entity = entity;
    NSSortDescriptor *sort = [[NSSortDescriptor alloc] initWithKey:@"occasion.dateOfEvent" ascending:NO];
    fetchRequest.sortDescriptors = [NSArray arrayWithObject:sort];
    fetchRequest.fetchBatchSize = 20;
    NSFetchedResultsController *theFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:managedObjectContext sectionNameKeyPath:@"occasion.dateOfEvent" cacheName:nil];
    self.fetchedResultsController = theFetchedResultsController;
    _fetchedResultsController.delegate = self;
    return _fetchedResultsController;
}

The second tab contains just the "Name" information. There is a plus button and when the user presses that, they're presented with a Modal View to add in the Name, Occasion, Amount and Date. They press save and it saves it to the Core Data Database. So the second tab displays only the names (and I had to do a lot of work to get this bit working so it's not duplicated) but the fetchedResultsController for the second Table View is:

- (NSFetchedResultsController *)fetchedResultsController
{
    NSManagedObjectContext *managedObjectContext = [self managedObjectContext];

    if (_fetchedResultsController != nil)
    {
        return _fetchedResultsController;
    }
    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
    NSEntityDescription *entity = [NSEntityDescription entityForName:@"Person" inManagedObjectContext:managedObjectContext];
    fetchRequest.entity = entity;
    NSSortDescriptor *sort = [[NSSortDescriptor alloc] initWithKey:@"name" ascending:NO];
    fetchRequest.sortDescriptors = [NSArray arrayWithObject:sort];
    NSFetchedResultsController *theFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:managedObjectContext sectionNameKeyPath:nil cacheName:nil];
    self.fetchedResultsController = theFetchedResultsController;        _fetchedResultsController.delegate = self;
    return _fetchedResultsController;
}

When I select a row, the prepareForSegue method gets called:

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{

    NSIndexPath *indexPath = [self.personTableView indexPathForCell:sender];
    Transaction *selectedTransaction = [self.fetchedResultsController objectAtIndexPath:indexPath];
    if ([segue.identifier isEqualToString:@"Selected Person Segue"])
    {
        EnvylopeSelectedPersonTableViewController *selectedPerson = [segue destinationViewController];

        [selectedPerson setTransaction:selectedTransaction];
    }
}

So in the SelectedTableView, I obviously have a property to hold that:

@property (nonatomic, strong) Transaction *transaction;

With the setter which sets it's title to be the selected name.

What I want in this new Table view is to show a history of just that person - so similar to the original first tab, but just tailored to this person, which is why I thought I'd use predicates.

The selected table view fetchedResultsController is:

- (NSFetchedResultsController *)fetchedResultsController
{
    NSManagedObjectContext *managedObjectContext = [self managedObjectContext];

    if (_fetchedResultsController != nil)
    {
        return _fetchedResultsController;
    }

    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];

    NSEntityDescription *entity = [NSEntityDescription entityForName:@"Transaction" inManagedObjectContext:managedObjectContext];
    fetchRequest.entity = entity;


    NSSortDescriptor *sort = [[NSSortDescriptor alloc] initWithKey:@"occasion.dateOfEvent" ascending:NO];

    fetchRequest.sortDescriptors = [NSArray arrayWithObject:sort];
    fetchRequest.fetchBatchSize = 20;

    fetchRequest.predicate = [NSPredicate predicateWithFormat:@"whoBy.name = %@", self.transaction.whoBy.name];

    NSFetchedResultsController *theFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:managedObjectContext sectionNameKeyPath:@"occasion.dateOfEvent" cacheName:nil];
    self.fetchedResultsController = theFetchedResultsController;
    _fetchedResultsController.delegate = self;
    return _fetchedResultsController;
}

This is creating an exception:

Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[Person whoBy]: unrecognized selector sent to instance 0x8b550b0'

I understand why this is happening; I am passing a Transaction and displaying a Transaction, but I cannot use a Transaction in the Person Table View because it creates duplicates for the person (that was the big problem I faced here), so how I can somehow caste this? The reason I need to use a Transaction fetchRequest in the Selected Table View is because I need to reference the date, the title and the amount, all of which are related to the Transaction and not part of the Person.

Any help on this would be massively appreciated!

Upvotes: 0

Views: 2047

Answers (1)

Martin R
Martin R

Reputation: 539965

In prepareForSegue:, the fetched results controller fetches Person objects, not Transaction objects. Therefore

Transaction *selectedTransaction = [self.fetchedResultsController objectAtIndexPath:indexPath];

is actually a Person (casting it to Transaction does not change this fact). So you should change this to

Person *selectedPerson = [self.fetchedResultsController objectAtIndexPath:indexPath];
EnvylopeSelectedPersonTableViewController *selectedPersonVC = [segue destinationViewController];
selectedPersonVC.person = selectedPerson;

where person is a property of EnvylopeSelectedPersonTableViewController:

@property (nonatomic, strong) Person *person;

The predicate to display all transaction for this person would then simply be

fetchRequest.predicate = [NSPredicate predicateWithFormat:@"whoBy = %@", self.person];

Upvotes: 1

Related Questions