Paul Calnan
I love it when a plan comes together
Published August 27, 2014

I use a lot of TextExpander snippets when doing Objective-C development. I really like Objective-C, but it can be pretty verbose at times. If I find myself typing the same thing repeatedly, I make a new snippet for it.

The snippets make extensive use of Xcode placeholder tokens. For example:

[NSString stringWithFormat:@"<#format string#>", <#args#>]

This will give you two placeholder "bubbles"; one for format string and the other for args. You can tab between the bubbles and replace them with whatever you need in that place.


I use ;init to insert a default initializer:

- (instancetype)init
    if ((self = [super init]))
    return self;

Notice the use of %| which defines the cursor location after the snippet is expanded.

I also use ;iinit to insert an initializer body (iinit for Inside Init):

    if ((self = [super init]))
    return self;

That's useful for writing initializers with parameters. It's indented an extra four spaces since it's going inside the method body.

Default Description Implementation

I use ;desc to generate a -description method that behaves like the default in NSObject. I can then build on it from there.

- (NSString *)description
    return [NSString stringWithFormat:@"<%@: %p>", NSStringFromClass([self class]), self];

UIViewController View Methods

;vwa generates a -viewWillAppear:

- (void)viewWillAppear:(BOOL)animated
    [super viewWillAppear:animated];

;vwd generates a -viewWillDisappear:

- (void)viewWillDisappear:(BOOL)animated
    [super viewWillDisappear:animated];

;vda generates a -viewDidAppear:

- (void)viewDidAppear:(BOOL)animated
    [super viewDidAppear:animated];

;vdd generates a -viewDidDisappear:

- (void)viewDidDisappear:(BOOL)animated
    [super viewDidDisappear:animated];


Variations for readonly, copy, strong, weak, and assign:

Abbreviation Content
;pnra @property (nonatomic, readonly, assign)
;pnrc @property (nonatomic, readonly, copy)
;pnrs @property (nonatomic, readonly, strong)
;pnrw @property (nonatomic, readonly, weak)
;pna @property (nonatomic, assign)
;pnc @property (nonatomic, copy)
;pns @property (nonatomic, strong)
;pnw @property (nonatomic, weak)

The Strong-Self / Weak-Self Dance

Abbreviation Content
;ws __weak typeof(self) weakSelf = self;
;ss __strong typeof(weakSelf) strongSelf = weakSelf;

Private Read-Write Property Declarations

Say you have a public interface for a class, like this:

@interface SomeClass : NSObject
@property (nonatomic, readonly, copy) NSString *someString;
@property (nonatomic, readonly, copy) NSDictionary *someDictionary;

If you want to generate a private declaration, simply copy the interface to the pasteboard and run ;dprivate. This is a shell script snippet, that performs the following:

pbpaste | sed -e 's/ : .*$/ ()/' -e 's/, readonly//' | grep -v '^[-+]' | grep -v '^$'

Using the example above, this would output:

@interface SomeClass ()
@property (nonatomic, copy) NSString *someString;
@property (nonatomic, copy) NSDictionary *someDictionary;

Mark Pragmas

Abbreviation Content
;mark #pragma mark -
;public #pragma mark - Public Methods
;private #pragma mark - Private Methods

Declare and Initialize an Object using the Default Initializer

;new uses a fill-in for the type name and variable name:

%filltext:name=typename% *%filltext:name=varname% = [%filltext:name=typename% new];

If you enter NSMutableArray and array, this will yield:

NSMutableArray *array = [NSMutableArray new];

Key-Value Observing Done Right

I've long since lost where I got these, but it's the correct way to register for KVO notifications.

Declare a context variable static to the source file with ;kvocontext:

static void *kKVOContext = &kKVOContext;

Register for KVO notifications in the initializer, viewWillAppear, etc. with ;kvoadd:

[<#(id)#> addObserver:self forKeyPath:<#(NSString *)#> options:<#(NSKeyValueObservingOptions)#> context:kKVOContext];

Deregister from KVO notifications in dealloc, viewWillDisappear, etc. with ;kvoremove:

[<#(id)#> removeObserver:<#(NSObject *)#> forKeyPath:<#(NSString *)#> context:kKVOContext];

And, finally, declare the KVO notification hander with ;kvomethod:

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
    if (context != kKVOContext)
        [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];

    if ([keyPath isEqualToString:<#(NSString *)#>])

Documents Directory

I used to have to look this up whenever I needed to use the documents directory. Now I use ;document:

NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *<#path#> = [documentsDirectory stringByAppendingPathComponent:<#filename#>];

The final NSString variable is the full path to a file in the documents directory.

Common Type Names

Abbreviation Content
;nsa NSArray
;nsd NSDictionary
;nss NSString
;nsma NSMutableArray
;nsmd NSMutableDictionary
;nsms NSMutableString

These also work with the ;new snippet, above.


Reference the default NSNotificationCenter using ;dnc:

[NSNotificationCenter defaultCenter]

Make a new NSString by calling -stringWithFormat: with ;swf. The %key:tab% at the end highlights the format string placeholder:

[NSString stringWithFormat:@"<#format string#>", <#args#>]%key:tab%

Make a constant string whose value is equal to the variable name with ;cstr:

NSString *const %fill:name% = @"%fill:name%";

Xcode Paths

These are useful in the Terminal or in the Go to Folder dialog in the Finder.

Abbreviation Content
;iossdks /Applications/
;osxsdks /Applications/
;simulator ~/Library/Application\ Support/iPhone\ Simulator
;archives ~/Library/Developer/Xcode/Archives
;deriveddata ~/Library/Developer/Xcode/DerivedData
Published August 12, 2014

The canonical Objective-C block syntax reference misses some cases that I use frequently. At the end of last year, as I was getting back into full-time Objective-C development, I made the following example file that covers every block usage I could think of. It is my go-to reference whenever I'm in doubt as to how to write a particular kind of block.

Published March 30, 2014

Here's another AppleScript I wrote. It would be great if Xcode would show the current Git branch and working directory status in the main window. Unfortunately, the only way to see the current branch is to select the Source Control menu. There's no quick way to see if you have uncommitted modifications.

I wrote this AppleScript, which I have bound to ⌥G using FastScripts. It pops up a notification showing the current branch and status.

This is a notification showing the master branch with no changes or additions:

Clean Working Directory

This is what it looks like when there are modifications:

Modified Files in Working Directory

And, this is the notification when there are modifications and additions:

Modified and Added Files in Working Directory

The script:

Published March 24, 2014

I'm on a bit of an AppleScript kick lately. It's a frustrating language to use as it's often counterintuitive. I'm constantly looking up the right syntax to do things. But, it's a great way to patch little annoyances you encounter in Mac software.

The latest annoyance involves Tweetbot image windows. While I'm reading my timeline, I click on lots of links and images. After a while, I find I have a ton of image windows left over.

There's no good way to close all of those windows without quitting and restarting the app. Holding down ⌥ and clicking on the Window menu shows Close All bound to ⌥W, but it doesn't work for me. The alternative is to click on each window and ⌘W on the keyboard, but who wants to do that?

I wrote a little AppleScript to do it. Tweetbot isn't strictly AppleScriptable, but using System Events and Accessiblity gets the job done. The script is a bit kludgy as the inner repeat loop fails after closing three or four windows. To get around it, I wrapped it in a try...on error block and repeat that until there's only one window left.

I've got this bound to ⇧W using FastScripts.

The script:

Published March 24, 2014

This seems to happen whenever someone on the team updates the provisioning profile and commits the change to the .xcodeproj file. The build fails and Xcode complains that it can't find a provisioning profile identified by a UUID.

To fix it, I have to stop what I'm doing, open Preferences, click the right Apple ID (I have one for each client), click the team, click View Details, click the refresh button, and then close out the action sheet and the Preferences dialog. Not the end of the world, but it gets old after a while.

To get around this issue, I wrote an AppleScript to automate the process. It's a bit fragile, as it's using Accessibility and System Events to poke around in the Preferences dialog. It probably will break in a later version of Xcode (it works in Xcode 5.1).

To use the script save it to your computer and set the appleID variable to your Apple ID. I use FastScripts to bind the script to ⌥R while in Xcode.

The script:

Older Posts