Mar 1 2010

ObjectiveCAnnotate – Simple Annotations for ObjectiveC

Today I’m open sourcing a project that we’ve been using at IdeaSwarm in one incarnation or another for about a year and a half. It’s called ObjectiveCAnnotate.

ObjectiveCAnnotate is a parsing/code-generation project written for and in Objective-C that saves us from writing a lot of boiler plate code from day to day. I hope that others find it useful.

The Problem

I came to Objective-C from Ruby. The first time I saw the @property syntax I expected it to be roughly the same at the attr_accessor syntax in Ruby; that is, I expected it to declare everything that I needed for working with a property.

I was quickly disabused of that notion. In order to get a property working you have to:

  • declare the member variable
  • add the @property syntax in the @interface
  • synthesize the property
  • release it if necessary, depending on the type

This struck me as very repetitive and I quickly grew tired of updating four places in my code each time that I decided to change a variable name. So I wrote some code to make my life easier.

My Solution

I built a little script that parses annotations, which are little bits of meta-data added to header and class files using comments, and outputs the boilerplate code that I’d come to know and love.

I’ve been using the Ruby version of this project for about a year and a half and over that time I’ve added other features:

  • declaring dynamic properties
  • externalizing methods in the header file and keeping the header and implementation in sync
  • private methods and properties

A Quick Example

Say you have a header file:

@interface Truck <NSObject>
{
    BOOL isShiny;
    NSString *licenseNumber;
}

@property (nonatomic) BOOL isShiny;
@property (nonatomic, retain) NSString * licenseNumber;
- (void) runSirens;
@end

@implementation Truck

- (void) runSirens
{
    //do something
}

@synthesize isShiny;
@synthesize licenseNumber;

- (void) dealloc
{
    [licenseNumber release];

    [super dealloc];
}   
@end

If you change any of the properties you have to update the associated statements. If you change the method signature you have to update it in the header file.

Using ObjectiveCAnnotate you have to type:

//@generate

@interface Truck <NSObject>
{
    //@properties
    BOOL isShiny;
    NSString *licenseNumber;
}

@end

@implementation Truck

//@extern
- (void) runSirens
{
    //do something
}

- (void) dealloc
{
    [self generatedDeallocForTruck];
    [super dealloc];
}   
@end

Now any properties that you add get declared and synthesize automatically, and if necessary they are released in the generated dealloc method that you call from the dealloc. Any change to the method signature is automatically reflected in the header file.

For now this is implemented by adding code in generated blocks to the source file. It adds some noise and the file ends up looking like the below:

@interface Truck <NSObject>
{
    BOOL isShiny;
    NSString *licenseNumber;
}

//generated block
@property (nonatomic) BOOL isShiny;
@property (nonatomic, retain) NSString * licenseNumber;
- (void) runSirens;
//end generated block
@end

@implementation Truck

- (void) runSirens
{
    //do something
}

//generated block
@synthesize isShiny;
@synthesize licenseNumber;

- (void) generatedDeallocForTruck
{
    [licenseNumber release];
}
//end generated block

- (void) dealloc
{
    [self generatedDeallocForTruck];

    [super dealloc];
}   
@end

Now any time you add or remove a property or change the method signature you make that change in one place and it is reflected everywhere.

The property syntax is flexible, handling object and non-object types, allowing overriding of property attributes (nonretain, copy, assign, etc), dynamic properties, private properties, etc.

It would be nice if there was a way of handling this that added somewhat less noise than the original syntax, but we’ve settled for being easier to develop with. I’ve tried a few other ways of implementing this and they’ve all fallen short one way or another.

Agh! A Rewrite!

I presented the framework at my local MN CocoaHeads a couple of weeks ago and someone asked if I’d ever thought of re-writing it in Objective-C. My answer was a pretty stern no.

Then I got to thinking. I was already planning on open sourcing the project at some point. It made a lot of sense to open source it in the language that it was working with. That would make it much easier for users to extend. How long could it take to re-write, considering that I already knew the code and requirements ? Ahh, estimates.

Turns out it took about a week and a half to re-write my Ruby script in Objective-C, document it, etc. I made a few improvements on the way, such as block property declarations and the like.

License

ObjectiveCAnnotate is released under a BSD license. That’s just the framework, your code belongs to you.

Get the Bits

Get it from bitbucket: ObjectiveCAnnotate

Documentation and how-to are on that site as well.

Feedback

File bugs: Bug Tracker

Google Group: Google Group

Comments are closed here while I figure out a little spam problem, so please use the group. If it becomes a problem there I’ll have to move to approving members individually, but it uses open enrollment for now.


Mar 5 2009

My Dubious Manifesto

I don’t want people to just like my software. I want them to love it; to crave it like they crave food and sex and illicit drugs and higher bandwidth.

I want users burning my icons into their foreheads with a custom made brand and jumping from buildings in the hopes that it will express their love.

I want evil geniuses in secret lairs with equally evil and inexplicably large felines to spend years working on secret death rays to carve my logo into the moon.

I want religious cults to be founded and subsequently go to war with each other over the finer interpretation of the bullet points in my feature list.

In short, I want to create software that inspires not passion but pathology.


Jan 23 2009

Exending Xcode – File & Project Templates, TextMate Style Macros

Introduction

I gave a presentation a few weeks ago at the local CocoaHeads MN meeting on extending Xcode. It was pretty well received and at some point I promised to deliver this content online in some form; this post is the result. The techniques here are all available in some form or another elsewhere on the web, but it took some digging to get them together in one place and working in Xcode 3.1. Hopefully this post will save some of you from having to do similar work, which while fascinating, may not be the best use of your time.

It should be noted that I am far from the most proficient Xcode user on the planet. There may be better ways to approach the following; if I’ve made a mistake somewhere please leave a comment to that effect and I’ll correct it.

I will be covering three ways to extend Xcode. First custom project and file templates, then the really useful custom text macros with placeholders.

Requirements

All of this work was done in Xcode 3.1. It was not tested with earlier versions and may not work there.

Custom Project Templates

Xcode comes with some great templates to get you up and running quickly with a variety of common application types. Early on in my iPhone development work when I was churning through a bunch of project ideas I found myself making the same modifications for code signing and basic setup again and again. Exactly the kind of work that I would normally automate with a build tool. I wanted to build Xcode project templates that had a number of these things baked in. It turned out to be pretty simple, once I got the directory structure worked out.

I’m going to walk through the following steps.

  • Create an override directory for Xcode
  • Copy and modifying an existing Project Template

Creating a User Override Directory

When Xcode boots it scans through a few different directories to put together its runtime environment. One of the last directories that it scans is ~/Library/Application Support/Developer/Shared/Xcode. This directory contains user specific Xcode extensions. If this directory does not exist, you will have to create it. This is where you will place project templates, file templates, and macro specifications.

Copying and Modifying an Existing Template

Xcode has a few different directories where it keeps overrideable runtime settings. For our purposes the most useful of these directories are /Developer/Library/Xcode/ (standard Xcode templates, plugins, etc) and /Developer/Platforms/iPhoneOS.platform/Developer/Library/Xcode (iPhone specific extensions). I’ll use the iPhone directory for my examples. It should look like this:

iPhone Directory Screenshot

The directory we’re interested in for now is the Project Templates directory. If you open up that directory you will see ‘Application’. Underneath that directory you will find the actual project template directories. These are just standard Xcode projects with a few small differences. Copy one of those folders, for example ‘iPhone Navigation-Based Application’.

Now create a Project Templates directory in your user overrides folder. Create a subfolder named whatever you like; I used Ideaswarm. Paste the project directory into this subfolder. Your directory should look like ~/Library/Application Support/Developer/Shared/Xcode/Project Templates/YourGroupingName/iPhone Navigation-Based Application. Now  go to Xcode and choose file -> New Project.

Notice the ‘User Templates’ section with the ‘YourGroupingName‘ grouping. This grouping will be named after the grouping directory under Project Templates. The template itself will be named after the Xcode project directory; renaming this folder from iPhone Navigation-Based Application will change the name of the template.

This project can now be opened like any Xcode project. Any changes you make will be reflected in the final project. Just remember to delete the build directory that Xcode will create when you open the project; that probably isn’t something you want to include in your template.

One other thing to note is the replacement of certain character sequences when a project is created from a template. The most important of these is  _PROJECTNAMEASIDENTIFIER_ which will be replaced with the new project name wherever it is found, including in file names.

Custom File Templates

Custom file templates are basically the same. You can copy an existing file template from /Developer/Platforms/iPhoneOS.platform/Developer/Library/Xcode/File Templates/Cocoa Touch Classes. For this example I am using NSObject subclass.pbfiletemplate. Create a directory called File Templates under your user overrides directory. Create another grouping directory inside this directory, named whatever you’d like, and paste the file template into this directory. You’ll end up with something like ~/Library/Application Support/Developer/Shared/Xcode/File Templates/YourGroupingName/NSObject subclass.pbfiletemplate. The ‘.pbfiletemplate’ is required.

Inside of this directory you will find three files:

  • class.h
  • class.m
  • TemplateInfo.plist

If you open up TemplateInfo.plist you will find the following three key value pairs:

{
    MainTemplateFile = "class.m";
    CounterpartTemplateFile = "class.h";
    Description = "my test template";
}
MainTemplateFile is the main file to create. CounterpartTemplateFileis an optional second file. Description is the description that will appear in the new file window. So if you change description to ‘my test template’ and open the new file dialog you will see a User Templates section with a grouping named YourGroupingName. Selecting this will show your file template.

Again, the name of the template directory (before the .pbfiletemplate part) will become the name of your file template.

If you read some of the existing file templates you can find some constants that will be replaced when the file is created. These will allow you to insert the new file name into the generated text (<<FILEBASENAMEASIDENTIFIER>>), for instance.

Custom TextMate – Like Macros

One of the things I missed most about TextMate was the easy tab based macro completion with placeholders. It turns out that Xcode actually has something similar, but it is relatively painful to learn how to add your own macros. I’ll be covering how to do this in this section.

The first thing you’ll want to do is take note of the code completion keys that you have set up in the Xcode preferences. These can be found under Xcode preferences -> Key Bindings -> Edit -> Next Completion, Completion List, and Select Next Placeholder. I remapped these to be on my home row because I use them so much.

To follow the pattern of the rest of this post, I will be taking some existing Xcode macros and bending them to my evil will. First create a Specifications directory in your user override directory: ~/Library/Application Support/Developer/Shared/Xcode/Specifications. Then you’ll need to find the xcode macro specifications. These are in /Developer/Applications/Xcode.app/Contents/PlugIns/TextMacros.xctxtmacro/Contents/Resources/. Copy the ObjectiveC.xctxtmacro file into your new Specifications directory. Any changes in this file will override the previous macro definitions; you are free to change existing macros or add more to your hearts content.

If you open your copy of  ObjectiveC.xctxtmacro you will see a long list of declarations similar to this:

 {
            Identifier = objc.try;
            BasedOn = objc;
            IsMenuItem = YES;
            Name = "Try / Catch Block";
            TextString = "gibberish";
            CompletionPrefix = "@try";
            IncludeContexts = ( "xcode.lang.objc.block" );
 }
Identifier - unique id for the macro. Doesn’t matter what this is, as long as it is unique.

BasedOn - This is a simple inheritance scheme. You can base one macro on another and any keys that are not declared will be pulled from the parent. Usually you will leave this as objc.

IsMenuItem - Determines whether or not this macro will show up in the Edit -> Insert Text Macro menu. Most of the time I set this to NO.

Name - This is the name the macro will appear under in the Edit -> Insert Text Macro menu if IsMenuItem is yet. Usually I put something descriptive in here regardless of whether it is a menu item, to make it easier to determine the purpose of the entry.

TextString - This is the string that your macro will expand to. You can ignore the funky syntax for now, I will explain it shortly. I’ve replaced it with ‘gibberish’ here, for clarity.

CompletionPrefix - this is the basic macro. If you type @try and then hit Next Completion, Xcode will replace this text with the TextString.

IncludeContexts/ExcludeContexts – these allow you to determine where your completion is valid. These are not necessary; if excluded your completion will work anywhere in an objective c file. In the above case, @try will only expand inside of a block.

Ok, lets create a very simple macro. Scroll to the bottom of this file and insert the following (make sure there is a comma after the previous entry):

{
           Identifier = objc.asimplemacro;
           BasedOn = objc;
           IsMenuItem = NO;
           Name = "My first Simple Macro";
           TextString = "NSString *something = @\"Simple\"";
           CompletionPrefix = simple;
}
Notice that the double quotes must be escaped. As simple as this is, it is what I get tripped up on the most. If Xcode encounters a parsing error like an unescaped double quote it will either 1) not load your file, using the original macros or 2) not load the macros after the error, resulting in some working and others not. It can be a bit frustrating to track down.

Now restart Xcode (every macro edit requires a restart), open an objective c file, and type ’simple’. Then use your ‘Next Completion’ key. It should replace the text with your NSString. In some cases you may have to go through a few completions to see your macro; this list includes function and variable name completions as well. If you keep your macros fairly unique, you won’t have this problem.

Now we will insert a custom placeholder. Replace the TextString line above with:

TextString = "NSString *<#varname#>= @\"<#text#>\"";
Restart Xcode and try your macro again. This time it should have two placeholders, varname and text. You should be able to jump between them using the Next Placeholder key.

And that’s it! Not quite as nice as TextMate, but a lot better than not having this functionality at all.

More Advanced Macro Techniques

Two other things I wanted to mention briefly.

First, if you use the same CompletionPrefix for two macros then Next Completion will cycle through them. This can be useful for defining two similar expansions with the same keyset. For Example, I use the obscure ‘df’ to first declare a CGFloat, then a float.

Second, IncludeContext and ExcludeContext. These allow you to define the same macro key and have it do different things in different places, for example the body of a function vs the function name, or the header vs the implementation. Some contexts I find useful:

"xcode.lang.objc.block"
"xcode.lang.objc.bracketexpr"
"xcode.lang.objc.parenexpr"
"xcode.lang.objc.interface"
"xcode.lang.objc.implementation",
"xcode.lang.objc.protocol.declaration",
"xcode.lang.objc.protocol",
"xcode.lang.objc.function.declaration",
"xcode.lang.objc.function.definition",
"xcode.lang.objc.typedef",
"xcode.lang.objc.initializer"
‘initializer’ is after an equals expression. The others are fairly self explanatory. Or seem so to me, after looking at them for so long.

Conclusion

That concludes our Xcode fun for the day. Let me know if you have any questions or need clarification on some points. Hopefully this helps some of you have a more enjoyable Xcode experience.


Jan 23 2009

Hello world!

Welcome to Turkeys Heart Rhinos, the oddly named technical blog of one Dylan Bruzenak (that would be me!). The theme and artwork (the turkey and rhino are coming, honest) are currently being hammered out, so expect a few things to change around here before I get everything exactly right. I apologize for the probably lacking blog skills in the meantime.

I expect posting frequency to be low and split between product announcements for my independent software development company, IdeaSwarm, and technical posts with a wider appeal. I’ll try to get everything tagged properly so that any rss followers don’t get a bunch of product spam they are not interested in.

Why Turkeys Heart Rhinos ? Why wouldn’t they ? I mean, who doesn’t love rhinos ? Also, it gives the blog two built in mascots. And if you’re wondering, yes, I’m a bit strange.

*cookie cutter template comment removed for CONTENT. Wouldn’t want to bruise your eyes, which is exactly what this new theme is not doing. Because it is beautiful.