Use categories in Objective C to add instance variables to a CCSprite or anything else

Ok, I’ve got this down to about 10 lines of code.

I’ve got some button sprites in Cocos2D that need an instance variable.  I’m already using userData, and found that I was starting to overload it to use it the text in there for more than one purpose… if you start stripping off substrings from the userData to code for things, you might be interested in having another instance variable.

So you want to add “.mydata” to your sprites?  Try this:

1.  Decide where you want the scope of this.  In my app, there’s only one scene with one layer, and so in the MyLayer.m file where all my button sprites are, I’m adding this code at the top, just above the @interface MyLayer line:

@interface CCSprite (MyExtras)
@property (nonatomic,retain) id mydata;
#import <objc/runtime.h>
static char const * const mydataKey = "mydata";
@implementation CCSprite (MyExtras)
@dynamic mydata;
- (id) mydata { return objc_getAssociatedObject(self, mydataKey); }
- (void) setMydata:(id)new { objc_setAssociatedObject(self, mydataKey, new, OBJC_ASSOCIATION_RETAIN_NONATOMIC); }

That’s 10 lines.

2. Test it out.  Something like this:

CCSprite* buttonTest = [CCSprite spriteWithFile:@"buttontest.png"];
[buttonTest setMydata:[NSNumber numberWithInt:6]];   //use official setter method
buttonTest.mydata = [NSString stringWithFormat:@"hello"];  //ok too

All this does is attach an object to your sprite button, and spit it back out into the console log.  It’s flexible… attach any object you want this way, pretty much.  The second line makes it a NSNumber object, and the 3rd line replaces that with a NSString.  The only restriction here is you have to give .mydata an ID… that means you can’t say:

[buttonTest setMydata:6];

Because 6 is an int, not an id.  Get around that by using NSNumber.

If you want to read more, there’s lots of blog posts out there about this topic (try “associative references” in Objective C), and how to add other methods to extend existing classes, etc.  But this is just one common use case, simplified to its essentials.


Real quick new Kobold2D templates

So, in the project I’m working on now, we need new Kobold2D templates every week or two.  Why?  It’s easier this way… the two of us code separately mostly in different parts of the app, then merge every few days or weekly, and going forward we make a new template to use.  Then we can start and try test apps with new features, and not worry about them.

Real quick new template procedure:

1.  Clean Up Your App:  You have a working app in Kobold2D that you want to use as your new template.  Great.  Now clean it up.  I recommend putting all your Resources neatly organized in a /Resources folder, and all your classes in a /Classes folder, inside your /Projectfiles folder.  Really.  Do this.  In a Finder window, so that the Groups of classes and Resources in XCode match the folder organization in Finder.  Feel free to have folders in folders.  Ok… is it clean and tidy?  Groups in XCode match folders in Finder exactly?  Ready to move on.

2. Make A New Template App:  Close XCode, and use Kobold2D Project Starter app to make a new app, named “_Code7-Template_” (this template will then show up as “Code7″ in the Kobold2D Project Starter app when you’re done).  Click Create Project From Template to start this up in XCode.   Use the Empty-Project template if this is your first template, or use your latest template as a starter, since that should be similar to this one structurally.

[You don't have to close XCode first, but it needs to restart.  Choose Revert usually, if it asks because you didn't follow my suggestion to close it.]

3. Clean Up The Template in XCode:   In your new “_Code7-Template_” app in XCode, open the Projectfiles group.  Get rid of the HelloWorldLayer and AppDelegate class files (select, right click, DELETE – not just remove references).  Do the same for the Resources group (including the config.lua file… you’re going to use a new one, right?).  Leave the “Supporting Files” group alone (main.m and two pch files).  These deletes will render the app useless and broken for the moment.  That’s ok.

4.  Close XCode.  You don’t want it open right now while you change things in Finder.

5.  Clean Up The Template in Finder:  In Finder, go to your new template app (something like: ~/Kobold2D/Kobold2D-1.0.1/_Code7-Template_/).  Clean out the Projectfiles folder.  It will probably still have a /Resources folder in it.  Get rid of it.  Leave the other three Supporting Files files.

6. Add Your New Files In Finder:  Open another Finder window and copy (Option-Drag-Drop) your neatly organized Classes and Resources folders full of goodies from your working app, into the Projectfiles folder of the Code7 template you just cleaned up.

7.  Add New Files in XCode:  Restart XCode with your Kobold2D workspace.  Navigate to the Projectfiles group of your Code7 app.  Right click on Projectfiles, and select the Add files to “_Code7-Template_” option.  Browse to your Projectfiles folder of the Code7-Template, and select the Classes folder, and add it, being sure to select Create Groups for any added Folders.  Repeat to add the Resources folder.

8.  Bingo.  Test your app in the simulator to make sure it works just like the one you’re copying from.  Now it’s ready to become the newest template in your arsenal.

9. Make it a template: Close XCode (or not).

(a) In Finder, copy your _Code7-Template_ folder over into the ~/Kobold2D/Kobold2D-1.0.1/__Kobold2D__/templates/project folder, where the other templates reside.

(b) Make a _description_ file, by copying one from another template into your template folder, opening it up in a text editor, and rewrite it to describe this template.  Save.

(c) You’re done.  If you’ll be sharing this template with anybody, get rid of your build products taking up space… right click on the _Code7-Template_.xcodeproj package, and select Show Package Contents to open it in a new Finder window.  Trash the xcuserdata folder, it’s unwanted now.

10.  Test it… use Kobold2D Project Starter app and your new template should show up.  Start a new app (Code8 of course) and keep moving.

I know, ten steps doesn’t sound so “real quick” but I’ve got the whole thing down to about 5 minutes now, because my app structure is clean and I know what to do.  You can too.