Search code examples
xcodecocoaxcode4cocoa-bindings

How to bind a 1:many relation in Interface Builder


I have a simple Core Data app with to model objects: employee and notes. Employees can have many notes (1:many), each note belongs to one employee (1:1). In the employee window I display a list (NSTableView) of employees on file using an NSArrayController. Clicking on an employee displays their details next to the list (name address, gender etc.). Now, I would also like to display a list (also NSTableView) of the notes on file for the selected employee. The list should display 2 attributes of each note on file: its topic and its entry date).

I tried to do the following: (1) bind the date column of the table to the Employee controller, controller key: selection, model key path: notes.date (2) bind the topic column of the Employee controller, controller key: selection, model key path: notes.topic

However this throws an exception (unrecognised selector).

How do I correctly bind the notes attribute of an employee to the NSTableView? Can it be done "directly" using cocoa bindings in IB or is additional code required to access the notes attribute of an employee before I can set up the bindings? I checked the data written by Core Data and this looks all good.

Many thanks for your help...

EDIT: here's the stack trace from the debugger...

2011-09-30 00:46:56.010 EmployeeDemo[3843:707] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[_NSFaultingMutableSet objectAtIndex:]: unrecognized selector sent to instance 0x10260b9c0'
*** First throw call stack: (
0   CoreFoundation                      0x00007fff8c654986 __exceptionPreprocess + 198
1   libobjc.A.dylib                     0x00007fff8b040d5e objc_exception_throw + 43
2   CoreFoundation                      0x00007fff8c6e05ae -[NSObject doesNotRecognizeSelector:] + 190
3   CoreFoundation                      0x00007fff8c641803 ___forwarding___ + 371
4   CoreFoundation                      0x00007fff8c641618 _CF_forwarding_prep_0 + 232
5   AppKit                              0x00007fff87c72648 -[NSTableBinder _visibleRowIndexesForObject:] + 267
6   AppKit                              0x00007fff87c72291 -[NSTableBinder observeValueForKeyPath:ofObject:change:context:] + 131
7   Foundation                          0x00007fff8dbb818a NSKeyValueNotifyObserver + 387
8   Foundation                          0x00007fff8dbb21af -[NSObject(NSKeyValueObservingPrivate) _notifyObserversForKeyPath:change:] + 756
9   AppKit                              0x00007fff876f1f0b -[NSController _notifyObserversForKeyPath:change:] + 206
10  AppKit                              0x00007fff877fc5fc -[NSController observeValueForKeyPath:ofObject:change:context:] + 847
11  AppKit                              0x00007fff879a1cf9 -[NSArrayController observeValueForKeyPath:ofObject:change:context:] + 98
12  Foundation                          0x00007fff8dbb818a NSKeyValueNotifyObserver + 387
13  Foundation                          0x00007fff8dbd9387 NSKeyValueDidChange + 486
14  Foundation                          0x00007fff8dc6834b -[NSObject(NSKeyValueObservingPrivate) _didChangeValuesForKeys:] + 631
15  CoreData                            0x00007fff8e827543 _PFFaultHandlerFulfillFault + 3619
16  CoreData                            0x00007fff8e825bd8 _PFFaultHandlerLookupRow + 1592
17  CoreData                            0x00007fff8e825254 _PF_FulfillDeferredFault + 212
18  CoreData                            0x00007fff8e8250d8 _sharedIMPL_pvfk_core + 56
19  CoreData                            0x00007fff8e85bd3e -[NSManagedObject valueForKey:] + 222
20  Foundation                          0x00007fff8dbb479e -[NSObject(NSKeyValueCoding) valueForKeyPath:] + 348
21  AppKit                              0x00007fff8799ded5 -[NSArrayController _multipleValueForKeyPath:atIndex:] + 84
22  AppKit                              0x00007fff8799cfc9 -[NSArrayController _singleValueForKeyPath:] + 151
23  AppKit                              0x00007fff87704cfe -[_NSControllerObjectProxy valueForKeyPath:] + 77
24  Foundation                          0x00007fff8dbb4761 -[NSObject(NSKeyValueCoding) valueForKeyPath:] + 287
25  AppKit                              0x00007fff87704baa -[NSBinder _valueForKeyPath:ofObject:mode:raisesForNotApplicableKeys:] + 654
26  AppKit                              0x00007fff87704894 -[NSBinder valueForBinding:resolveMarkersToPlaceholders:] + 171
27  AppKit                              0x00007fff87907352 -[NSValueBinder _referenceBindingValue] + 31
28  AppKit                              0x00007fff87907163 -[NSValueBinder _adjustObject:mode:observedController:observedKeyPath:context:editableState:adjustState:] + 647
29  AppKit                              0x00007fff87906e48 -[NSValueBinder _observeValueForKeyPath:ofObject:context:] + 303
30  AppKit                              0x00007fff8791ed0b -[NSTextValueBinder _observeValueForKeyPath:ofObject:context:] + 43
31  Foundation                          0x00007fff8dbb818a NSKeyValueNotifyObserver + 387
32  Foundation                          0x00007fff8dbb21af -[NSObject(NSKeyValueObservingPrivate) _notifyObserversForKeyPath:change:] + 756
33  AppKit                              0x00007fff876f1f0b -[NSController _notifyObserversForKeyPath:change:] + 206
34  AppKit                              0x00007fff8795e7a5 -[NSArrayController didChangeValuesForArrangedKeys:objectKeys:indexKeys:] + 120
35  AppKit                              0x00007fff879601bd -[NSArrayController setContent:] + 870
36  AppKit                              0x00007fff879a2664 -[NSArrayController(NSManagedController) _performFetchWithRequest:merge:error:] + 370
37  AppKit                              0x00007fff87bb149e -[NSObjectController(NSManagedController) fetchWithRequest:merge:error:] + 177
38  AppKit                              0x00007fff87bb1522 -[NSObjectController(NSManagedController) _executeFetch:didCommitSuccessfully:actionSender:] + 92
39  AppKit                              0x00007fff87d932e7 _NSSendCommitEditingSelector + 30
40  AppKit                              0x00007fff87a50246 -[NSController _controllerEditor:didCommit:contextInfo:] + 188
41  libdispatch.dylib                   0x00007fff847df90a _dispatch_call_block_and_release + 18
42  libdispatch.dylib                   0x00007fff847e177a _dispatch_main_queue_callback_4CF + 308
43  CoreFoundation                      0x00007fff8c5e9c0c __CFRunLoopRun + 1724
44  CoreFoundation                      0x00007fff8c5e9216 CFRunLoopRunSpecific + 230
45  HIToolbox                           0x00007fff871924ff RunCurrentEventLoopInMode + 277
46  HIToolbox                           0x00007fff87199c21 ReceiveNextEventCommon + 355
47  HIToolbox                           0x00007fff87199aae BlockUntilNextEventMatchingListInMode + 62
48  AppKit                              0x00007fff876de191 _DPSNextEvent + 659
49  AppKit                              0x00007fff876dda95 -[NSApplication nextEventMatchingMask:untilDate:inMode:dequeue:] + 135
50  AppKit                              0x00007fff876da3d6 -[NSApplication run] + 463
51  AppKit                              0x00007fff8795852a NSApplicationMain + 867
52  EmployeeDemo                        0x0000000100001a62 main + 34
53  EmployeeDemo                        0x0000000100001a34 start + 52
)
terminate called throwing an exception

EDIT 2:

It seems that there's another problem which has to do with the underlying model. The notes for an employee are entered via a separate window with its own controller. It creates the new note and allows the user to set its properties (topic, body text). When the user presses the "Save" button the saveNote method is executed:

- (IBAction)saveNote:(id)sender {
[[self note] setValue: [[self topicField] stringValue] forKey: @"topic"];
[[self note] setValue: [[self textView] string] forKey: @"bodyText"];
[[self note] setValue: [self employee] forKey: @"employee"];
NSError *error = nil;
if (![[self managedObjectContext] save:&error]) {
            //todo: add proper error handling
    NSLog(@"An error occurred while trying to save the memo");
}
[[self window] close];

}

When I step through this method the exception is actually generated when setting the relation between the note and the employee (3rd line).


Solution

  • What you need is an NSArrayController whose content is bound to the relationship. Then you bind the NSTableView content to the NSArrayController. From there you can do the columns.

    UPDATE

    This tutorial from Apple might help you out.