Challenge 2: Adding sorting


I have a problem with the second challenge. The App works perfectly fine, but I am not able to add the sorting programmatically. If I am right, the only method to implement is the last before Challenge 1 description right? If so, do I have to do something else (connection or attribute settings in IB)? Glad for any help!


Here’s a hint. There’s an NSTableViewDataSource method that you need to implement:


I implemented this as shown in the book:

- (void)tableView:(NSTableView *)aTableView sortDescriptorsDidChange:(NSArray *)oldDescriptors {
    NSArray *newDescriptors = [tableView sortDescriptors];
    [employees sortUsingDescriptors:newDescriptors];
    [aTableView reloadData];

but I sort of do not understand what I am doing here. The method is triggered when I click the header of the TableView and within the sortDescriptor is changed. So far so good. What is it doing now? It stores the tableViews sortDescriptors in a new variable. What are these sortDescriptors? Wether ascending or descending? Why descriptorS? I mean, I can only have one or not? Then I sort my array with this descriptor, then reload the tableView. Expect that I am a little confused by the newDescriptors thing, this seems logic. But it doesn’t work. I guess I have to do something in IB with the tableView but I don’t know what. I mean I have to tell the tableView that it should support sorting…


Working on my own understanding (I had a similar question). Here’s what I’ve found.

I added


When I run it I get

…sort descriptors: (
"(personName, ascending, caseInsensitiveCompare:)"


digging through the docs, I came across the NSSortDescriptor Class Reference. This clears it up a bit:

So, you need at least the first two (key path, sort order) and the selector is optional. Then NSSortDescriptor will send it on to the class of the object to get compared. I still can’t tease out the answer to my other question: viewtopic.php?f=180&t=3714 I’m not sure where “compare:” gets sent for processing. Oh, unless…Oh. string.length is now a number, so NSSortDescriptor is going to use NSNumber, which does have a compare: method. Ok, that’s tricky, but cool. This is that dynamic message passing decisions made at runtime that I’ve been reading about.



@DaPhil: Your implementation of tableView:sortDescriptorsDidChange: looks fine. If it’s not working, check that you have set the Sort Key and Selector on the table columns. You’ll get one sort descriptor for each column, ordered according to priority.

@markdf: sortUsingDescriptors: compares objects (a and b) by sending the compare: message (or another selector, if specified) to the objects and sorting accordingly: [a compare:b]. Because of how KVC works, [a valueForKey:@“length”] will return an NSNumber, which can be compare:d. So basically the magic is thanks to KVC.


Another question:
How does the sorting work? I mean, when I put as sorting key for example expectedRaise, then the table looks for [objectInTheColumn valueForKey:“expectedRaise”] and sorts appropriately. But, where does it get the object that is in the column? I support the table with it’s content via

- (id)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row {
    if ([[tableColumn identifier] isEqualToString:@"Name"]) {
        return [[employees objectAtIndex:row] personName];
    } else if ([[tableColumn identifier] isEqualToString:@"Raise"]) {
        return [[NSNumber alloc] initWithFloat:[[employees objectAtIndex:row] expectedRaise]];
    } else {
        NSLog(@"Error while writing in TableView!");
        return nil;

but here, a value from an object is returned, not the object itself. I tried to put in the expectedRaise sorting key for the first column (names) and it worked. So it seems whole object is somehow known by the column. Am I right? Is my question even understandable?



Is there a way to set Sort Key and Selector programatically ?

During my struggle, I had used

NSSortDescriptor *ageDescriptor = [[NSSortDescriptor alloc] initWithKey:@"personName" ascending:YES]; NSArray *sortDescriptors = [NSArray arrayWithObject:ageDescriptor]; [employees sortUsingDescriptors:sortDescriptors];

to get sorting done after a new employee was added. I failed to reverse the sorting order by clicking on header of table column. Later I removed the above code and used

- (void)tableView:(NSTableView *)atableView sortDescriptorsDidChange:(NSArray *)oldDescriptors { NSArray *newDescriptors = [tableView sortDescriptors]; [employees sortUsingDescriptors:newDescriptors]; [tableView reloadData]; [atableView reloadData]; }
and set the Sort Key and selector for the column.