Tuesday, August 31, 2010

RussPack LITE for iPhone


RussPack LITE is now available for all of you who want to try the game for free before you buy the full version :) To find out more about RussPack, please visit the web site.

I decided to add iAds to this edition which was surprisingly easy. Since this subject is covered quite well in many other blog posts, I decided not to write a post about here in my blog.

I used information from Ray Wenderlich's post and the high quality feedback he got through comments from his readers to enable iAds in RussPack LITE. I chose a much simpler path than he did though, since I just added a ADBannerView "on top" of some of my menu screens and the get ready screen which pops up before each level.

I decided not to put iAds on the actual game screen for two reasons; a) it would have required a redesign of the layout, which would have been very hard since I already had used up all screen estate and b) it would have been quite annoying for the player :)

I should also mention that I updated both RussPack and RussPack LITE to version 1.1 because of an embarassing bug I found and becase the game was listed in AppStore as requiring iOS 4.0+ even though I had not used any 4.0 specific features. It turned out this was my own fault since you have to be careful to set the "deployment target" correctly before submitting the game to AppStore. This is in fact also covered in Ray's post that I mentioned above so go pay him a visit right away to avoid making the same mistake I did.

Go ahead and download the free version of the game now - I'll promise you that it's worth a test drive :)

Saturday, August 14, 2010

RussPack for iPhone



My first iPhone app is finally available on AppStore. It's a puzzle game called RussPack and it demonstrates many of the techniques explained in this blog. So if you need some motivation in order to learn how to develop for the iPhone I suggest you check it out! :)

If you have had any use of the information in this blog, I would appreciate highly if you bought RussPack from AppStore, as a small token of appreciation.

See www.russpack.com for more information about the game and how to buy it.

If anyone wonders why it took so long for me to release my first app - almost 1,5 years - it's beacause I got a little baby boy at almost the same time as I started learning how to develop for the iPhone. Having a baby leaves little time for fun stuff like blogging and coding, but as you can see I managed to find some time. Mostly on the bus and subway on the way to my work actually ;)

Thanks for your attention and hope you like the game - it's actually really fun and addictive!


Friday, March 12, 2010

Revisited: Storing and retrieving information using plists

Quite a while back I wrote a post about storing and retrieving information using plists. This has turned out to be one of the most appreciated posts in my blog, both by the number of readers and the number of comments. My post also contained some errors, but friendly commenters have helped eachother out in order to resolve those errors.

When I wrote the original post I hadn't really resolved how to store information on a real device in the proper way. For example, if you have a file in your application bundle which you want to update, you should start by copying the file from the bundle to the Documents-directory.

To resolve the issues from the first post and show the "proper way" of handling files that are updated by the application I simply created a new Window-based Application and edited the "applicationDidFinishLaunching" method to look like this:

- (void)applicationDidFinishLaunching:(UIApplication *)application {

// Override point for customization after application launch
[window makeKeyAndVisible];


// get the path to the "Documents" directory
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];

// get the path to our plist ("Documents/foo.plist")
NSString *plistPath = [documentsDirectory stringByAppendingPathComponent:@"foo.plist"];

// read or create plist

NSMutableDictionary *dict;
// check if our plist already exists in the Documents directory...
NSFileManager *fileManager = [NSFileManager defaultManager];
if ( [fileManager fileExistsAtPath:plistPath] ) {
// ...if it does, read it
NSLog(@"dict existed, reading %@", plistPath);
dict = [NSMutableDictionary dictionaryWithContentsOfFile:plistPath];
} else {
// ...if it doesn't, create it
NSLog(@"dict didn't exist, creating...");
dict = [NSMutableDictionary dictionaryWithCapacity:1];

// Fill the dictionary with default values, either by copying
// a default plist from our bundle to the Documents directory
// or simply creating a new dictionary and writing it to th
// Documents directory. Here we choose to create a new
// dictionary rather than providing a default plist in the bundle.

// create a NSNumber object containing the
// integer value 1 and add it as 'key1' to the dictionary.
NSNumber *number = [NSNumber numberWithInt:1];
[dict setObject:number forKey:@"key1"];

// write dictionary to Documents directory...
NSLog(@"writing to %@...", plistPath);
[dict writeToFile:plistPath atomically:YES];
}

// dump the contents of the dictionary to the console
NSLog(@"dumping...");
for (id key in dict) {
NSLog(@"key=%@, value=%@", key, [dict objectForKey:key]);
}

// check if key2 is present
NSString *value2 = (NSString *)[dict valueForKey:@"key2"];
if ( value2 == nil ) {
NSLog(@"key2 didn't exist, adding...");
[dict setObject:@"default-2" forKey:@"key2"];
}

// dump the contents of the dictionary to the console
NSLog(@"dumping...");
for (id key in dict) {
NSLog(@"key=%@, value=%@", key, [dict objectForKey:key]);
}
}

The code should be rather self-explanatory thanks to the inline comments, but I really recommend you to read my first post on this subject to really understand what is going on.

A thing worth commenting on is the really strange path names logged to the console. These pathnames can look like this:

2010-03-12 09:09:15.557 Plist2[10780:20b] writing to /Users/henrik/Library/Application Support/iPhone Simulator/User/Applications/E10D7186-1219-4C52-804A-FE217F760967/Documents/foo.plist...

This path is a path in the Mac OS filesystem used by the iPhone simulator when testing an application. The strange part of it is the long string of hexadecimal numbers starting with E10D7. This is the so called GUID which is unique for each installed application. A lot has been written about this in other sources on the Internet, so I won't go further into that:





One annoying thing about it and that is that it changes each time you recompile your application. That means, that each time you build and test your application the files created by the application will be somewhere else in the Mac OS filesystem. This makes it hard to inspect the files written by your application. Fortunately, modern versions of XCode copy the contents from the "old path" to the "new path" so even though the GUID changes, the files created by the application the last time are available the next time you build and test it.

If you couple this annoying thing with the fact that nothing is logged to the console if you exit your application by pressing the home-button in the simulator and then relaunch the application by pressing its icon in the simulator. This means that if you want to see what is logged to the console you are forced to relaunch the application from XCode, which will create a new GUID and thus a new path in the Mac OS filesystem.

However, since XCode nowadays copies the contents from the old to the new path it is possible to test how the application responds to "external changes" such as removing a file stored by the application, etc. This can be very convenient to perform effecient testing of an application.

For the application in this post you can test how it behaves if "foo.plist" is present or not by first running the application from XCode (CMD-Return), observing which path is logged to console, removing that file and then re-running the application from XCode (CMD-Return). This will give you something like this on the console:

[Session started at 2010-03-12 10:18:24 +0100.]
2010-03-12 10:18:27.456 Plist2[10888:20b] dict existed, reading /Users/henrik/Library/Application Support/iPhone Simulator/User/Applications/DB3B1A75-BF08-461E-919A-DBFFCC994036/Documents/foo.plist
2010-03-12 10:18:27.464 Plist2[10888:20b] dumping...
2010-03-12 10:18:27.468 Plist2[10888:20b] key=key1, value=1
2010-03-12 10:18:27.469 Plist2[10888:20b] key2 didn't exist, adding...
2010-03-12 10:18:27.471 Plist2[10888:20b] dumping...
2010-03-12 10:18:27.475 Plist2[10888:20b] key=key1, value=1
2010-03-12 10:18:27.477 Plist2[10888:20b] key=key2, value=default-2

[Session started at 2010-03-12 10:19:38 +0100.]
2010-03-12 10:19:40.159 Plist2[10897:20b] dict didn't exist, creating...
2010-03-12 10:19:40.180 Plist2[10897:20b] writing to /Users/henrik/Library/Application Support/iPhone Simulator/User/Applications/5B1E8FF3-45D6-443B-BB0B-2073EAE22520/Documents/foo.plist...
2010-03-12 10:19:40.191 Plist2[10897:20b] dumping...
2010-03-12 10:19:40.198 Plist2[10897:20b] key=key1, value=1
2010-03-12 10:19:40.199 Plist2[10897:20b] key2 didn't exist, adding...
2010-03-12 10:19:40.199 Plist2[10897:20b] dumping...
2010-03-12 10:19:40.200 Plist2[10897:20b] key=key1, value=1
2010-03-12 10:19:40.202 Plist2[10897:20b] key=key2, value=default-2