Can't add text to the text field

Thanks in advance everyone!

I’ve quite thoroughly gone over the code and interface builder connections in this chapter and I’m practically certain that I have everything right. Despite this, when running TahDoodle I still can’t add text to the text field. The “Add Task” button works fine and creates a default “Table View Cell” task when clicked, but when I click or double click on the text bar to add a named task the cursor doesn’t appear. Thereafter when attempting to type my computer simply produces an error sound. I’m using Xcode 7.2.1. What am I missing?

Here is my code:

Document.h

#import <Cocoa/Cocoa.h>

@interface Document : NSDocument
    <NSTableViewDataSource>

@property (nonatomic) NSMutableArray *tasks;

@property (nonatomic) IBOutlet NSTableView *taskTable;

- (IBAction)addTask:(id)sender;

@end

Document.m

#import "Document.h"

@interface Document ()

@end

@implementation Document

#pragma mark - NSDocument Overrides

- (instancetype)init {
    self = [super init];
    if (self) {
        // Add your subclass-specific initialization here.
    }
    return self;
}

- (void)windowControllerDidLoadNib:(NSWindowController *)aController {
    [super windowControllerDidLoadNib:aController];
    // Add any code here that needs to be executed once the windowController has loaded the document's window.
}

+ (BOOL)autosavesInPlace {
    return YES;
}

- (NSString *)windowNibName {
    // Override returning the nib file name of the document
    // If you need to use a subclass of NSWindowController or if your document supports multiple NSWindowControllers, you should remove this method and override -makeWindowControllers instead.
    return @"Document";
}

- (NSData *)dataOfType:(NSString *)typeName error:(NSError **)outError {
//    // Insert code here to write your document to data of the specified type. If outError != NULL, ensure that you create and set an appropriate error when returning nil.
//    // You can also choose to override -fileWrapperOfType:error:, -writeToURL:ofType:error:, or -writeToURL:ofType:forSaveOperation:originalContentsURL:error: instead.
//    [NSException raise:@"UnimplementedMethod" format:@"%@ is unimplemented", NSStringFromSelector(_cmd)];
//    return nil;
    
    //This method is called when out document is being saved. You are expected to hand the caller an NSData object wrapping our data so that it can be written to the disk. If there is noa rray write out an empty array
    if (!self.tasks) {
        self.tasks = [NSMutableArray array];
    }
    
    // pack the tasks array into an NSData object
    NSData *data = [NSPropertyListSerialization
                    dataWithPropertyList: self.tasks
                    format:NSPropertyListXMLFormat_v1_0
                    options:0
                    error];
    // Return the newly packahed data-object
    return data;
}

- (BOOL)readFromData:(NSData *)data ofType:(NSString *)typeName error:(NSError **)outError {
//    // Insert code here to read your document from the given data of the specified type. If outError != NULL, ensure that you create and set an appropriate error when returning NO.
//    // You can also choose to override -readFromFileWrapper:ofType:error: or -readFromURL:ofType:error: instead.
//    // If you override either of these, you should also override -isEntireFileLoaded to return NO if the contents are lazily loaded.
//    [NSException raise:@"UnimplementedMethod" format:@"%@ is unimplemented", NSStringFromSelector(_cmd)];
//    return YES;
    
    // This method is called when a document is being
    // You are handed an NSData object and expected to pull our data out of it
    // Extract the tasks
    self.tasks = [NSPropertyListSerialization
                  propertyListWithData: data
                  options: NSPropertyListMutableContainers
                  format:NULL
                  error];
    
    // return success or failure depending on the success of the above call
    return (self.tasks != nil);
}

# pragma mark - Actions

-(void) addTask:(id)sender
{
    // If there is not an array yet, create one
    if (!self.tasks) {
        self.tasks = [NSMutableArray array];
    }
    
    [self.tasks addObject:@"New Item"];
    
    // -reloadData tells the table view to refresh and ask its dataSource (which happens to be this BNRDocument object in this case) for new data to display
    [self.taskTable reloadData];
    
    // -updateChangecount: tells the application whether or not the document has unsaved changes, NSChangeDone flags the document as unsaved
    [self updateChangeCount:NSChangeDone];
}

#pragma mark Data Source Methods

-(NSInteger)numberOfRowsInTableView:(NSTableView *)tv
{
    // this table view displays the tasks array so that the number of entries in the table view will be the same as the number of objects int the array
    return [self.tasks count];
}

-(id)tableView:(NSTableView *)tableView
objectValueForTableColumn:(NSTableColumn *)tableColumn
           row:(NSInteger)row
{
    // Return the item from tasks that corresponds to the cell that the table view wants to display
    return [self.tasks objectAtIndex:row];
}

-(void)tableView:(NSTableView *)tableView
  setObjectValue:(id)object
  forTableColumn:(NSTableColumn *)tableColumn
             row:(NSInteger)row
{
    // When the user changes a task on the table view update the tasks array
    [self.tasks replaceObjectAtIndex:row withObject:object];
    
    // And then flag the document as having unsaved changes
    [self updateChangeCount:NSChangeDone];
}

@end

There are two flavors of table views on the Mac: Cell-based and View-based. The book uses Cell-based table views, which have since been deprecated, making View-based table views the Xcode default. In order for this exercise to work with the latest versions of Xcode, you’ll need to manually change the table view content mode to “Cell Based”; see Figure 32.9 in the book.

If that doesn’t work, zip up the project folder and attach it to a post, and we’ll have a look.

~Mikey