Search code examples
objective-ccore-datanstableviewnsarraycontroller

How can I populate an NSTableView from Core Data via an NSArrayController?


Originally I was populating my NSTableView via an outlet and had set the dataSource of the table to my controller class. I'm trying to switch to using an NSArrayController so I can enable sorting by column in my app.

In IB, I've added an Array Controller object. I have the Sort Descriptors binding connected to Shared User Defaults Controller so that the sorted column can be persisted between launches of my app. I have each column of my table bound to the Array Controller, the controller key is set to 'arrangedObjects' and the Model Key Path is set to the name of the field that should be rendered.

My data is coming from Core Data, and the entity I am trying to display has a relationship to another entity. An attribute on the 2nd entity needs to be displayed as the value for one of the table columns. Anyone have any thoughts/suggestions for me on what I'm missing here?

MainWindowController.h

#import <Cocoa/Cocoa.h>
#import "Notification.h"

@class AppDelegate;

@interface MainWindowController : NSWindowController <NSTableViewDataSource, NSTableViewDelegate> {
    AppDelegate <NSApplicationDelegate> *appDelegate;
}

//@property NSMutableArray *userNotifications;

@property (weak) IBOutlet NSTableView *notificationsTable;
@property (weak) IBOutlet NSArrayController *notificationsController;

@end

MainWindowController.m

#import "AppDelegate.h"
#import "MainWindowController.h"
#import "Utils.h"

@implementation MainWindowController

//@synthesize userNotifications;

@synthesize notificationsTable;
@synthesize notificationsController;

- (void) doubleClick:(id)sender
{
    NSInteger row = [notificationsTable clickedRow];

//  Notification *clickedNotification = [userNotifications objectAtIndex:row];
//  Notification *clickedNotification = 

//  [appDelegate redirectToBrowser:clickedNotification];
}

- (id) initWithWindowNibName:(NSString *)windowNibName
{
    self = [super initWithWindowNibName:windowNibName];
    if (self) {
//      userNotifications = [[NSMutableArray alloc] init];
        appDelegate = (AppDelegate *) [[NSApplication sharedApplication] delegate];
        [notificationsController setManagedObjectContext:[appDelegate managedObjectContext]];
        [notificationsController setEntityName:@"Notification"];
        [notificationsController setAutomaticallyPreparesContent:YES];

        [notificationsController fetch:self];
        [notificationsTable reloadData];
    }
    return self;
}

- (void)windowDidLoad
{
    [super windowDidLoad];

    // Implement this method to handle any initialization after your window controller's window has been loaded from its nib file.

    [notificationsTable reloadData];
}

- (void)awakeFromNib
{
    [notificationsTable setTarget:self];
    [notificationsTable setDoubleAction:@selector(doubleClick:)];
}

Solution

  • There are two solutions to this issue:

    1. Add an App Delegate object to IB by adding a new object and setting the class to AppDelegate.
    2. Assign the App Delegate as an instance variable appDelegate of the controller rendering your window by adding the below code to your .h and .m files:

    Controller.h

    @class AppDelegate;
    
    @interface Controller : NSWindowController {
        AppDelegate <NSApplicationDelegate> *appDelegate;
    }
    

    Controller.m

    #import "AppDelegate.h"
    
    - (id) initWithWindowNibName:(NSString *)windowNibName
    {
        self = [super initWithWindowNibName:windowNibName];
        if (self) {
            appDelegate = (AppDelegate *) [[NSApplication sharedApplication] delegate];
        }
        return self;
    }
    

    In either case, you'll need to add a new binding for the Array Controller. Navigate to the Binding pane in the Inspector and set the Managed Object Context (under Parameters) to Bind to (1) the App Delegate or (2) the File's Owner, then set the Model Key Path to (1) self.managedObjectContext or (2) self.appDelegate.managedObjectContext