Tag Archives: Mac Development

Apple changes Developer Program

Screen Shot 2015-06-09 at 11.18.20With the introduction of watchOS yesterday at WWDC2015, Apple have made some changes to their developer programme: it is no longer required to enrol for membership in the iOS and Mac developer programmes for $99 each. Now there’s only one membership, called Apple Developer for $99 per year. How exciting!

Apple were nice enough to add up the remaining days in all your enrolments and extend your membership until such time. From then on, only $99 per year are applicable, and not 2x $99 as previously.

The new Apple Developer membership also includes the previously free Safari Extension programme, as well as new resources for Swift 2 and watchOS.

All that’s required to go ahead is to sign a new agreement and save some cash. In my opinion, this is seriously good news. I was contemplating of letting my Mac Dev Membership lapse – now I don’t have to worry about that anymore 🙂





How to use Categories in Objective-C

Objective-C has a feature called Categories. These are used to extend existing classes. The principle is different than subclassing in that a Category will behave like the original class with additions, rather than a completely different class.

For example, if we’d subclass NSString then the resulting class name would be something like YourStringClass. We would hence need to instantiate it with [[YourStringClass alloc]init]. By contrast, a Category is instantiated and used like the original class (such as [[NSString alloc]init], however additional methods we provide are henceforth included.

We need separate header and implementation files for our Category, just like for a regular class. Apple recommend to name such files by starting with the superclass, followed by a plus sign, followed by your Category name.

In this example we’ll create a Category for NSString called Test. Hence we need NSString+Test.h and .m files. Xcode 6 no longer provides a mechanism for that, so either empty .h and .m files will suffice, or alternatively create a standard subclass and overwrite the relevant sections.

Here’s NSString+Test.h:

@import Foundation;

@interface NSString (Test)

- (NSString *)myModification;

@end

Instead of separating our class name from the superclass with a semicolon, we’ll use brackets. We add one public method here. We’ll write the code for it in NSString+Test.m:

#import "NSString+Test.h"

@implementation NSString (Test)

- (NSString *)myModification {
    
    // add your code here
    return @"Result!";
}

@end

No big surprises here either: we simply import the header file and write our method – a simple one will do here, returning “Result!”. Notice the way we declare the @implementation, again with brackets.

Here’s how we call the extended class from somewhere else, for example a view controller:

#import "ViewController.h"
#import "NSString+Test.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {

    [super viewDidLoad];
    
    // call my own method
    NSString *result = [[NSString alloc]init];
    NSLog(@"%@", [result myModification]);
}

@end

After importing the Category we can instantiate an NSString object and use our own method as if Apple had baked it right into the Foundation Framework. How cool is that?

Singletons

Categories appear to be even cooler when we implement a singleton. That’s a method we can call on the class without having to instantiate it first. While the above example makes little difference beside convenience, when we call something like [[NSString myKillerExtension] it really feels as if our code is part of the Foundation Framework.

Defining a singleton works just like defining a standard method, but instead of the minus sign we use a plus sign. I’m also demonstrating how to take a parameter in our method:

// singleton signature in header file
+(NSString *)specialInstruction:(NSString *)parameter;

// and in the implementation file
+(NSString *)specialInstruction:(NSString *)parameter {
    
    // singleton implementaion
    NSString *result = [NSString stringWithFormat:@"The parameter was %@.", parameter];
    return result;
}

We can now call the singleton as it it was part of the NSString class:

- (void)viewDidLoad {
    [super viewDidLoad];
    
    // call my singleton
    result = [NSString specialInstruction:@"testing"];
    NSLog(@"%@", result);
}

Now go forth and categorise!





How to check if the Main Window in your Mac App was closed

As is customary with other parts of Mac and iOS, the Main Window (or in fact any NSWindow) conforms to the NSWindowDelegate protocol. Sadly this isn’t mentioned in the class reference for NSWindow and you’ll have to guess.

The drill is the same:

  • hook up your window to a class as The Delegate
  • have the class conform to the protocol
  • implement the method you want to watch

In this example we’d like to see if our Main Window is being closed and react accordingly (for example, by closing down our super simple one window app). We’ll use AppDelegate as our Window Delegate:

@interface AppDelegate : NSObject <NSApplicationDelegate, NSWindowDelegate>

Now that we’re conforming to the protocol, let’s listen to the following:

#pragma mark - Window Delegate

- (void)windowWillClose:(NSNotification *)notification {
    
    NSWindow *currentWindow = notification.object;
    if ([currentWindow isEqualTo:self.window]) {
    
        // save context and exit
        [self saveAction:self];
        exit(0);
    }
}

The if statement is optional, but in essence we’re asking “is the window that’s being closed self.window” which AppDelegate already has a reference to. If that’s the case, then we go ahead and save our Managed Object Context and quit the app.

If your class is the NSWindow delegate for more than one window, give each an identifier and query it accordingly.

All that remains to be done is to hook up our window to AppDelegate as a delegate:

Screen Shot 2014-05-09 at 16.59.52





How to populate an NSTableView in code

Screen Shot 2014-05-04 at 11.22.52I’ve previously shown you how to populate an NSTableView using Bindings and an array controller. Today I’ll show you how to do it in code.

It’s relatively simple and very similar to powering a UITableView. The main difference is that in Cocoa we don’t have a controller object that comes bundled with our view, so we’ll have to create one manually (or use an existing class). Here are the steps:

  • add an NSTableView to your XIB file
  • create a controller class based on NSObject (I’ll call mine TableController)
  • drag an NSObject object into the XIB (that blue box)
  • associate it with your custom controller object (i.e. TableController)
  • set the data source and implement its methods

In this example I’m using the numbers 1-10 in two arrays: as numbers, and as written out values – so that two columns of the table view can be populated with different items.

#pragma mark - Custom Initialisers

- (NSArray *)numbers {
    
    if (!_numbers) {
        _numbers = @[@"1", @"2", @"3", @"4", @"5", @"6", @"7", @"8", @"9", @"10"];
    }
    return _numbers;
}

- (NSArray *)numberCodes {
    
    if (!_numberCodes) {
        _numberCodes = @[@"One", @"Two", @"Three", @"Four", @"Five", @"Six", @"Seven", @"Eight", @"Nine", @"Ten"];
    }
    return _numberCodes;
}

Next we’ll implement the following two methods: the first for returning the number of rows, and the second for returning the relevant data in each row:

#pragma mark - Table View Data Source

- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView {
    
    // how many rows do we have here?
    return self.numbers.count;
}

- (id)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row {
    
    // populate each row of our table view with data
    // display a different value depending on each column (as identified in XIB)
    
    if ([tableColumn.identifier isEqualToString:@"numbers"]) {
        
        // first colum (numbers)
        return [self.numbers objectAtIndex:row];
    
    } else {
        
        // second column (numberCodes)
        return [self.numberCodes objectAtIndex:row];
    }
}

Notice that in the second method we’re not referencing the columns with a number (like we do with rows). That’s because the user could reorder or remove columns. Instead we’re using the column identifier which you can set in IB.

Reacting to selections

If you want to react to user selections in your code you need to implement the NSTableViewDelegate Protocol and also connect your table view’s delegate to the above class. Then simply implement this method and you’re notified when a user clicks a row:

#pragma mark - Table View Delegate

- (void)tableViewSelectionDidChange:(NSNotification *)notification {
    
    NSTableView *tableView = notification.object;
    NSLog(@"User has selected row %ld", (long)tableView.selectedRow);
}

Unlike in iOS, we’re not directly passed the table view object – but we can grab it from the notification. Note that the log message will be off by one because the rows start at 0 rather than 1.

Watch the Screencast

Here’s a demo project with the above steps implemented:





How to remove focus from an NSTextField

Sometimes you need to make your text field give up first responder status, and therefore end editing. You’d think that – looking at the NSControl Class Reference – we should call something along the lines of validateEditing or abortEditing. But that doesn’t work.

Instead, we can set our text field’s window to “no first responder”. This approach works with any NSControl element:

[self.textfield.window makeFirstResponder:nil];

This approach is similar to telling a UITextField to resignFirstResponder (the old “invisible button to dismiss the iOS keyboard” ploy).





How to create an Alert View in Cocoa

Much like a UIAlertView, you can run an NSAlert on Mac like this:

// run a modal alert
NSAlert *alert = [[NSAlert alloc]init];
[alert addButtonWithTitle:@"Excellent"];
[alert setMessageText:@"This is your message."];
[alert runModal];

Or, if you prefer a much simpler one-liner you can use NSRunAlertPanel:

NSRunAlertPanel(@"Title", @"This is your message.", @"OK", nil, nil);

Your App Icon will be displayed in the window. Here’s an example:

Screen Shot 2014-05-01 at 18.20.43

The result will be the same: a separate modal window is brought up, waiting for the user to dismiss it with any of the buttons. The latter method will return an NSInteger to indicate which button was pressed.

Alert Sheets

Instead of the Alert Panel, we can also run an Alert Sheet. The functionality is the same, but instead of being a floating window the Alert Sheet is attached to your main application window. This is what it looks like:

Screen Shot 2014-05-03 at 10.10.31

You can create Alert Sheets like this:

NSAlert *alertSheet = [NSAlert alertWithMessageText:@"Title goes here." defaultButton:@"OK" alternateButton:nil otherButton:nil informativeTextWithFormat:@"And your message goes here goes here. Nice."];
    [alertSheet beginSheetModalForWindow:self.window modalDelegate:nil didEndSelector:nil contextInfo:nil];

The above method relies on a delegate to be called so you can evaluate which button was clicked. Alternatively – if you prefer blocks and evaluate the result right there and then – you can call the Alert Sheet with

[alertSheet beginSheetModalForWindow:self.window completionHandler:^(NSModalResponse returnCode) {        
    // check returnCode here
}];