views:

399

answers:

3

I am having trouble getting an core-data backed NSArrayController to work properly in my code. Below is my code:

pageArrayController = [[NSArrayController alloc] initWithContent:nil];
    [pageArrayController setManagedObjectContext:[self managedObjectContext]];
    [pageArrayController setEntityName:@"Page"];
    [pageArrayController setAvoidsEmptySelection:YES];
    [pageArrayController setPreservesSelection:YES];
    [pageArrayController setSelectsInsertedObjects:YES];
    [pageArrayController setClearsFilterPredicateOnInsertion:YES];
    [pageArrayController setEditable:YES];
    [pageArrayController setAutomaticallyPreparesContent:YES];
    [pageArrayController setSortDescriptors:[NSArray arrayWithObject:[NSSortDescriptor sortDescriptorWithKey:@"index" ascending:YES]]];
    BOOL result = [pageArrayController setSelectionIndex:0];

When I attempt to call setSelectionIndex:, it returns YES, indicating that the selection has been successfully changed. However, any subsequent getSelectionIndex calls to the pageArrayController object returns NSNotFound.

What I don't understand is that if I put the NSArrayController into a NIB, and allow the NIB file to perform the initialization (with all of the same attributes in Interface Builder), the NSArrayController works correctly.

Why is there a difference in behavior? Does the NIB file initialize these types of objects in a special way? Is my initialization of the NSArrayController incorrect?

Any help is appreciated. Thanks.

A: 

As far as why there may be a difference in behavior:

  1. Nib files store serialized objects using NSCoder.
  2. You are probably using binding on the IB side of things, where in your code you are setting the managed object context directly using a set method.

Maybe you could try something like the following in your code:

[pageArrayController bind:@"managedObjectContext"
                 toObject:self
              withKeyPath:@"managedObjectContext"
                  options:nil];

I don't have Xcode near by otherwise I would try somethings. Hopefully this gives you some clues to get you going in the right direction.

intregus
A: 

From where are you creating/configuring your array controller? The Core Data stack may not be ready yet, therefore your call to [self managedObjectContext] may be returning nil.

Also, why are you creating it programmatically if you can do it just fine with Interface Builder? The tool is there and works well (and eliminates many possible coding errors), so unless you have a good reason not to use it, you're not doing yourself any favors.

Joshua Nozzi
+2  A: 

Yes, nibs do initialize objects in a special way and sometimes it can be hard to figure out how to replicate that. I struggled with this too and finally found the answer in Apple's Core Data Programming Guide >> Core Data and Cooca Bindings >> Automatically Prepares Content Flag (thanks to Dave Fernandes on the Cocoa Dev list). The answer is that if you initialize an arraycontroller with nil content, you need to perform a fetch as well.

BOOL result;
NSArrayController *pageArrayController = [[NSArrayController alloc] initWithContent:nil];
[pageArrayController setManagedObjectContext:[self managedObjectContext]];
[pageArrayController setEntityName:@"Page"];
NSError *error;
if ([pageArrayController fetchWithRequest:nil merge:YES error:&error] == NO) 
     result = NO;
else
{
     //do all that other pageArrayController configuration stuff
     result = [pageArrayController setSelectionIndex:0];
}

BTW, [NSSortDescriptor sortDescriptorWithKey:@"index" ascending:YES]] raises a warning.

Elise van Looij
Also, your use of [self managedObjectContext] implicates that you have added your pageArrayController methods to the appDelegate. This is not considered good practice. You really should look into creating a separate page controller object (and model and view objects, as necessary to implement the MVC pattern) that will take care of the whole page functionality of your application. The page controller or page model objects may call [[NSApp delegate] managedObjectContext] when necessary.
Elise van Looij
Thanks for this solution, it works perfectly. My app structure is much nicer now that I can create these array controllers in code rather than relying on nibs.
CJ