views:

112

answers:

2

Hello,

I have a problem with Core Data which has left me at the end of my tether. So I was wondering if any of you wonderful people could help.

Basically, I have two entities in a Core Data project. These are:

Author {authorName, dateOfBirth}
Book {bookName, pages}

There is a one-to-many relationship from author to books called 'authors', and an inverse relationship called 'books'. I have exported the subclasses of these entities, and created my fetch controller. Do I now have to define the relationship programatically?

A list of authors is currently displayed in my table view. At the moment, I can only display a list of ALL the books in my Core Data project when I tap on an author. How would I go about accessing a list of books from a particular author? I am presuming I would use an NSPredicate, the logic I have for that so far is:

NSPredicate *predicate = [NSPredicate predicateWithFormat:@"ANY authors.bookName LIKE[cd] %@", authorName];

... but I am pretty sure that is incorrect.

I have been crawling the web for hours for an answer and remain confused. So any help would be greatly appreciated.

Many thanks :)

A: 

The member variable "books" exists in your Author class and points to an NSSet of NSManagedObject records, all of which will be Book objects.

Obviously you're able to get an author object, so to get a list of books by that author given the author object:

NSManagedObject *author = [self getSelectedAuthor];
NSSet *booksByAuthor = [author valueForKey:@"books"];
NSArray *booksArray = [booksByAuthor allObjects]; 

You can then use the NSArray to populate a UITableView.

XCode has the ability to automatically create Managed Object Classes for your Core Data entities. Open your xcdatamodel file, select one of the Entities, then File -> New. You should see Managed Object Class as an option. This will let you create Author and Book classes as subclasses of NSManagedObject. Those classes have each attribute of the entity defined as a property, which means the code above could read:

Author *author = [self getSelectedAuthor];
NSSet *booksByAuthor = author.books;
NSArray *booksArray = [booksByAuthor allObjects];
John Franklin
I'm sorry, but I've no idea how this helps me.
Martyn
Does that help?
John Franklin
Do I do this in my detail view controller or my view controller? Also, where has 'getSelecterAuthor' come from?
Martyn
No matter what I try, using what you have wrote just crashes my app.
Martyn
This would go in the author table view delegate's tableView:didSelectRowAtIndexPath:. The getSelectedAuthor is a pseudocode abstraction in this example to get the author object the user selected. You have the Author object somewhere already, else you wouldn't be able to populate the author UITableView. The booksArray should be handed to your detail table view.
John Franklin
Thanks for the reply. I have taken onboard what you said, but when I NSLog the passed booksArray, it appears to be empty "booksArray: ( )" I have the following code in my tableView:didSelectRowAtIndexPath NSManagedObject *selectedObject = [[self fetchedResultsController] objectAtIndexPath:indexPath]; Author *author = selectedObject; NSSet *booksByAuthor = author.books; NSArray *booksArray = [booksByAuthor allObjects]; myDetailViewController.booksArray = booksArray; But like I said, the passed array is empty?
Martyn
I've managed to sort the problem now. Nethertheless, thanks for you help!
Martyn
A: 

It's likely that you have something like the following:

@interface AuthorListController : UITableViewController {
    UITableView *table;
    NSArray *authors; // array of NSManagedObjects
}

@property (nonatomic, retain) IBOutlet UITableView *table;
@property (nonatomic, retain) NSArray *authors;

And in your implementation you have:

-(void)tableView:(UITableView*)tableView didSelectRowAtIndexPath:(NSIndexPath*)indexPath {
    NSManagedObject *author = [authors objectAtIndex:indexPath.row];
    // create and show your book-level view here
}

Now that you've got the selected author object, you can get the books (assuming you've set up the one-to-many relationship in the Data Modeler) directly:

NSSet *books = author.books;

But if you didn't have the author object loaded, for whatever reason, you might build a new search and use the books->authors relations in your predicate:

NSManagedObjectContext *objectContext = self.managedObjectContext;
NSEntityDescription *entity = [NSEntity entityForName:@"Books" inManagedObjectContext:objectContext];
NSFetchRequest *fetch = [[NSFetchRequest alloc] init];
[fetch setEntity:entity];

NSPredicate *predicate = [NSPredicate predicateWithFormat:@"author.name == %@", authorName];
[fetch setPredicate:predicate];

// ... create your error object and execute the fetch, etc
Seamus Campbell
I don't have an NSArray of 'authors'? I am simply using the default navigation controller core data template.Also, by 'author.name' do you mean 'author.authorName'? I don't have a simple '.name'.Thanks.
Martyn
I managed to fix the problem now.I used 'authorName == %@", authorName'. Thanks for the help!
Martyn