Developer Notes

NOTE: This page has been replaced by the new blog.

Unit Tests for the iPhone

In XCode 3.0 (not sure when they added it), there are options to add Unit Tests to your iPhone application but I was not able to get this to work properly using the iPhone Unit Test target. However I did find a reference to add Cocoa Unit Tests to your iPhone project. If you follow the link, you will find all the information you need. It's quick to add to your project and will allow your tests to run every time you build.

Google Framework

After some further work, the above setup allows easy running of unit tests. However, debugging those Unit Tests is another story. I found another page that walks through using the Google Toolkit for Mac to do unit testing. This allows you to run the unit tests as an executable and at least output console output (still can't seem to debug it). Note that with this framework, you need to set the active executable to your Testing executable in order to run. Any NSLog calls will be output to the console.

Unit Test Assertions

The table below is a quick reference to the different types of assertions that can be done in the UnitTest.

OCUnit Macro Purpose
STAssertNil(a1, description, ...)Generates an error if a1 is not nil.
STAssertNotNil(a1, description, ...)Generates an error if a1 is nil.
STAssertTrue(expression, description, ...)Generates an error if expression does not evaluate to true
STAssertFalse(expression, description, ...)Generates an error if expression does not evaluate to false
STAssertEqualObjects(a1, a2, description, ...)Generates an error if a1 is not equal to a2. Both must be Objective-C objects.
STAssertEquals(a1, a2, description, ...)Generates an error if a1 is not equal to a2. Both must be C scalar values.
STAssertEqualsWithAccuracy(left, right, accuracy, description, ...)Generates an error if a1 and a2 are not within a certain amount of each other. Primarily for use with floats and doubles to take into account small rounding errors due to the way they store values.
STAssertThrows(expression, description, ...)Generates an error if expression does not throw an exception.
STAssertThrowsSpecific(expression, specificException, description, ...)Generates an error if expression does not throw an exception of a specified class.
STAssertThrowsSpecificNamed(expr, specificException, aName, description, ...)Generates an error if expression doesn’t throw an exception with a specific name.
STAssertNoThrow(expression, description, ...)Generates an error if expression throws an exception.
STAssertNoThrowSpecific(expression, specificException, description, ...)Generates an error if expression throws an exception of a specified class.
STAssertNoThrowSpecificNamed(expr, specificException, aName, description, ...)Generates an error if expression throws an exception with a specific name.
STFail(description, ...)Generates an error.
STAssertTrueNoThrow(expression, description, ...)Generates an error if expression is false or if it throws an exception.
STAssertFalseNoThrow(expron, description, ...)Generates an error if expression is true or if it throws an exception

Table 1: Macros Available in OCUnit from Apple

Secure Hashes

If your application requires generation of secure hashes, you can use the raw Crypto APIs. These are actually C calls and do not have nice Objective C objects to interface with. The code snippet below shows how this is done for SHA-1. The input is any NSString and the output is a hex-encoded NSString version of the hashcode. If you need other types of hashing besides SHA-1, then you will need to change the SHA1 hames in the functions and constants.

+ (NSString*) getHash:(NSString *) text
{
    NSData* myData = 
         [text dataUsingEncoding:NSStringEncodingConversionAllowLossy];
    CC_SHA1_CTX ctx;
    uint8_t * hashBytes = NULL;
    NSData * hash = nil;
    
    // Malloc a buffer to hold hash.
    hashBytes = malloc( CC_SHA1_DIGEST_LENGTH * sizeof(uint8_t) );
    memset((void *)hashBytes, 0x0, CC_SHA1_DIGEST_LENGTH);
    
    // Initialize the context.
    CC_SHA1_Init(&ctx);
    // Perform the hash.
    CC_SHA1_Update(&ctx, (void *)[myData bytes], [myData length]);
    // Finalize the output.
    CC_SHA1_Final(hashBytes, &ctx);
    
    // Build up the SHA1 data.
    hash = [NSData dataWithBytes:(const void *)hashBytes 
                          length:(NSUInteger)CC_SHA1_DIGEST_LENGTH];
    
    // if could not create, then exit
    if(hashBytes)
    {
        free(hashBytes);
        return nil;
    }
    
    // now return this as an hex-encoded string
    NSMutableString *stringBuffer = [NSMutableString
                                     stringWithCapacity:([hash length] * 2)];
    const unsigned char *dataBuffer = [hash bytes];
    int i;
    for (i = 0; i < [hash length]; ++i)
    {
        [stringBuffer appendFormat:@"%02X", (unsigned long)dataBuffer[i]];
    }
    
    return [[stringBuffer copy] autorelease];
}

Let There Be Lite

Creating a Lite or Free version (don't call it a 'Demo' on the App Store) of your application is pretty easy to do assuming there your application can support reduced functionality. One way you could do this is just copy your whole project and start modifying your code to make it Lite. The biggest drawback to this is the dual maintenance of code if you have to fix a bug in both the full and Lite versions. The alternative is to create a new target in the same XCode project with different settings. This is the approach I took with Flik.

Fortunately for me, a Lite version of Flik translates into providing just a few levels instead of the full set of levels and so I could focus on those changes.The steps below outline what was done.

Change Your Code

Where your Lite version differs from the full version at the code level, you should add #ifdef areas that are applicable to the Lite version versus the full version. While you can do an explicit #define in your code and change this before compilation, see the 'XCode Build Settings' below to see how to have XCode do this for you.

#ifdef LITE
    // LITE logic goes here
#else
    // FULL logic does here
#endif
					

To test that your targets are setup correctly, add this code somewhere:

#ifndef LITE
#error "Not Lite"
#endif // LITE

That will cause your build to FAIL on all non-Lite builds, and SUCCEED (assuming no other compile issues) on all Lite builds.

Copy Your Target

In XCode, expand the Targets item and select your application. Right click and say duplicate. At this point you probably should rename this to end in 'Lite' or 'Free'. You may want to rename the Info.plist file as well.

Show Targets

XCode Build Settings

For each Build Configuration that you need (e.g. Debug, Debug Device, Device Ad Hoc Distribution, Device App Store Distribution), you need to change the following settings for the Lite version. This can be found on the Build tab of the Project Settings for your Lite Target (select your target and then select the Project > Edit Active Target ... Lite).

  • Info.plist File
  • Product Name

You can actually specify "Configuration: All Configurations" in the top left of that screen to avoid the hassle of doing this multiple times.

  XCode Settings

One other item to change on this tab is the Preprocessor Macro. This is the same as doing a #define in your code. See the section aboe on where this can be used in code.

Macros

XCode Properties Settings

If you add a space in your product name, this causes an issue in the Identifier field on the Properties tab. This normally starts out with: com.yourcompany.${PRODUCT_NAME:identifier}. If your name is 'App Lite', then this will fail during the upload process to Apple. You should explicitly set this to something unique, following Apple's recommended scheme.

Macros

While you are on this screen, you should change the Icon File to the file you wlll create next.

Graphics and Other Files

Flik Lite

You should create a different icon to be displayed on the App Store. The simple way to do this is just add the word 'Lite' to it. You should make sure that this icon is added to just your Lite target and not your full target. Conversely, you should also make sure the Lite icon is not in the full target.

targets

You should do this with all the other images or files that should be in one version and not another. You also need to prepare other images for the iTunes App Store.


That should be it. At this point you should be able to build both your full and Lite versions of your application easily. Just change the Active Target before you build.

2D in a 3D world

Flik was originally developed as a pure 2D game drawing the sprites on a background image and then showing that at 30 fps. This allowed me to use the 'standard' coordinate system I was used to (0,0 being in the upper left) as well as some previous work I had done. Needless to say, this worked great in the simulator!

Of course, the simulator had the full power of my MBP behind it so when I finally got an actual device (I was holding out for the iPhone 3G), I realized that while the iPhone could update the screen at 30 fps, it wasn't built for it. In fact, when I did this the iPhone stopped responding to any touches probably caused by starving the other threads in the system.

At first, I thought I had an issue with handling the touches but after googling for a while (back in the days when there was the NDA and no official forums) I realized that I needed to switch to OpenGL to run at 30+ fps reliably. I was originally going to rewrite my game engine to use Core Animation, but I decided to make the jump to OpenGL to make sure that it would run fast enough. This was a big leap to go from a very fixed 2D model where I was certain of my viewport and locations into one where it was initially unknown on how to do the precise positioning I would need.

As it turns out, Apple ships very good example code and the CrashLanding was exactly what I needed to start from. It provides a simple 2D engine on top of OpenGL. From this code I was quickly able to creating OpenGL versions of my 2D sprite engine classes. Since the interfaces between the classes were largely the same, once I finished the port everything worked together just as it had before (yet another case where spending the time to learn Objective C and its class/object system was a great idea). Of course there were a few issues (like the Y axis is reversed), but I was able to encapsulate these inside the corresponding classes so they weren't a factor.

Now that its all done, I can crank up the framerate to 60 fps without a problem (although I eventually lowered this to 30 fps to conserve battery-life, I hope).

Good Objective-C Links

I found the following sites extremely useful in helping answer my Objective-C questions:

Cocoa Dev has some great information on tracking down memory allocation/deallocation issues (I'll have to do a post on that later). Highly recommended!

The Objective-C Curve

...well until I hit that wall that is Objective-C. Back in the day I did a lot of Smalltalk so its messaging/function calling scheme brought back some memories, although its intertwining with C was a little different. Like most languages, learning the syntax is fairly straightforward - once you grasp the rules you can correlate things back to other languages you've used. Since it uses C syntax for the basics (conditionals, loops, etc), that made some things easier.

Then I decided to tackle the class system. Coming from languages like Java and C#, there are some confusing aspects. The first one was object instantiation and construction. By starting to read some sample code, I would always see things like [[Foo alloc] init] or [[Foo alloc] initWithSomeMonkey: mky]. Once you dig a little deeper, you can see that alloc is the same as new and init* is equivalent to a constructor in those other languages. After that there was the understanding of where to place member variables, public functions and private functions. Actually that last bit is an interesting one, since you actually define your private functions in your .m file using a category.

For example, you have a Level.h file that looks like:

// simple Level object representing a Game Level
@interface Level : NSObject {
	NSString* levelName;
}
// public property (e.g. setter/getter) for level name
@property (readwrite, retain) NSString* levelName;

// draw the level in the given rectangle
- (void) drawLevel: (CGRect) viewRect;

@end
					

In your source file you need to define another interface for your private methods and then two implementations!

// this is the category where you insert provate functions
// conveniently called 'private'
@interface Level (private) {
	// oooh, private function!
	- (void) mySecretFunction;
}
@end

// implementation of the functions defined in the Level.h file
@implementation Level

// the compiler will auto generate setters/getters
// with the @synthesize call
@synthesize levelName


- (void) drawLevel: (CGRect) viewRect
{
	//magic happens here
}

@end

// implementation of the functions in the private category
@implementation Level (private)

- (void) mySecretFunction
{
	// secret magic happens here
}

@end
					

Another thing to note in the above example is the way you need to declare properties to auto-generate, which is a new feature in Objective-C 2.0.

Starting iPhone Development

So I decided to do iPhone development. The two big learning curves to get over were the XCode system and the Objective-C language. XCode itself was not too much of an issue (although there were things I was used to in Eclipse and VisualStudio that caused some initial grief). I have to admit that I have not done any Interface Builder (IB) work. This was mostly due to the fact that when I started playing with the SDK, there was no IB and all the samples I referred to initially were all about creating UIs programatically. Of course having something like 14 years of Java experience helps since I just did the visual layout in my head (the only way to really build Java UIs is by hand so this is second nature to me).

XCode itself is a fairly complete tool from Apple and its free! It has the compiler, editor, profiles, leak detectors, and a nice iPhone simulator (although that last one only comes with the iPhone SDK which is currently for a fee). XCode also has built-in source code control support, so I was easily able to set this up to use my Subversion server and now I was off and running...