Tuesday, July 26, 2011

Install Xcode 3 on Lion

I'm not going to post anything but this link:



I needed this because I still use Property List Editor to edit and save binary plist and strings files. If anyone knows how to do this with Xcode 4, please let me know.

NOTICE: I tried to open my .strings file AFTER I opened and saved it in Xcode 4 and it was corrupt! YIKES!, fortunately I made a copy before and was able to open that file. So initially there is a problem with binary plists (a strings file is just a plist) inside Xcode 4.1 on Lion. The Xcode 3 Property List Editor can read / write plain (text) plists and binary format.


Monday, July 25, 2011

10.7 and iOS Production Environment

Well over the weekend I made the switch to 10.7 Lion. I performed a clean install, mostly because my 10.6 had more crap installed than I care to admit. About the only thing I may miss is MacPorts / ImageMagik.

I checked several blogs and noticed the trend was posting how to make Lion like 10.6 SL.... ????

Yeah, guess we're a bit afraid of change, aren't we.. ?

So far I'm all set after just a couple of days; YES I can scroll backwards, er I mean natural scrolling ;-)

Just one thing that is going to give me Carpal Tunnel and that is with my MagicPad I used to be able to tap-tap and drag to select text or drag an item in Finder. Yeah, that's gone, so now about all I can do as a replacement is to enable three-finger drag... um yeah I'm not sure how long before that feels natural... I look like someone with, oops sorry, need to be politcally correct. We'll leave it at it sure feels awkward.

My iMac is now one revision old, a 3.06GHz 21.5" model and it wails with 10.7 installed but I'm a firm believer in clean installs. :-)

So I've been mostly working today and I'm nearly used to the new scrolling method and am in the process of build and testing with Xcode v4.1 (love the new analyzer!). I hope to submit YAU (yet another update) after v1.2.2 gets approved (WARP gce) so I'll post any problems. Just remember to deselect the System Tools on your Beta Xcode installs and you should be golden. Oh yeah... made a trip to Terminal to "mkdir /Beta" since the Xcode installer wouldn't let me create a new root folder, eh?

Friday, July 22, 2011

Know Success Even When it Slaps You in the Face!

AKA: Hyper WARP gce Development Report

Hyper WARP gce was developed because:
1: I love Space Trading games
2: I needed to get a handle on a full Game Center integration
3. I needed to get a handle on adding consumable IAP's in an app

While I did not write this down, (which I think all your goals should be written down) I loosely defined success for the project as 500 Game Center players within 30-days. Yeah I realize Tiny Tower has, what, 1.2 million! I like to keep my goals attainable and realistic! ;-) I also looked at a bunch of other indie Game Center games and was surprised by the low number of players.

Sorry, I digress. I kept track each day the number of Game Center players and we broke the 500 mark 20 days in and after 31 days we had 701 players. Now these weren't all active players, but they did play and report scores. For completeness it is day 34 and we have 742 total Game Center players (4200+ total downloads).

Just think if I had spent more than $0.00 on marketing... ;-)


In addition, we now have a "tiny", in relative App Store numbers, group of core players, that even while the current version of the app crashes, in multiple areas (yeah, epic failure on my part!), they LOVE PLAYING IT. They have also provided GREAT FEEDBACK (thanks guys and girls!)

Note: Some of the crashes only come after playing the game for like 90 minutes (to clarify that's 90 minutes without a pause!) <- pretty sure that's another type of success!

Take Aways:
1. Game Center support is an absolute MUST HAVE for your game
2. If YOU don't LOVE playing your game, why would anyone else?
3. Leverage the work done and shared by others (don't reinvent the button!)
4. If you give something away if you app crashes, plan on it crashing ;-)
5. Quick turn around (start then finish in a few turns) games are very popular. It was 5-days before submission before we added the 3 Jump Challenge (most popular, of course it's first game mode), prior the shortest game play was 5 Jumps.
6. You can have a successful app while NOT supporting older devices (um yeah, if your device doesn't multi-task, sorry, no WARP gce for you).

In the Credits we list all the shared code used but I'd be remiss in not thanking TWO projects that were critical in the development of WARP gce.
1. ASTStoreKit : by Anystone Technologies
2. GameKitHelper: by Steffen Itterheim

Don't be afraid to push the envelope; Hyper WARP gce has what I believe are some app store "firsts":
* In App Consumables can be transferred to any other iOS device (buy a 10-pack then send 5 to another device)
* An Achievements leaderboard. With 40 achievements we wanted to hook as many "types" of players and this way you can compete at the achievement level and see how well other players are doing.
* First "good" Space trader game in Game Center ;-)

WAIT - you don't know what Hyper WARP gce is? So you haven't played it yet? Come on, it's FREE, give it a try. App Store Link.

Monday, July 11, 2011


Available on GitHub:

Ever wish you could apologize to your user after your app crashed? Me too.

Please note this is designed for MULTI-TASKING DEVICES ONLY!

Enter CrashCatcher, no it does nothing to prevent or "catch" crashes or errors in your app; I'll leave that up to you. What it does is simply logs that the app started up and logs that the app did resign properly.

When it doesn't resign properly, well that's a crash.

Check it out on GitHub, includes working demo.

Sunday, July 10, 2011

Come on guys!

For the love of god! Fellow developers:
1: if you support rotation in you app, for christs sake, rotate it and test it!
2: if you use networks services, test your app on cellular AND an EDGE connection! <- especially if you are trying to show me your wares!
3: if you offer a universal app, make sure it functions properly on a real iPad!
[End of rant]

Okay, I know I feel better, how about you?

Saturday, July 9, 2011

Show Developer Notices

So while we are deep in the forest, putting out fires, inspecting the underbrush... sometimes... I know just every so often, um, we forget to "undo" our developer hacks / changes.

I now call this method from applicationDidBecomeActive ... to help :-)

In a common header file I have:

#define kAllowCheats                    1   // 1 on, 0 off

In my game manager I have this:
    // Developer notices
    if (getenv("NSZombieEnabled") || getenv("NSAutoreleaseFreedObjectCheckEnabled")) {
        NSLog(@"NSZombieEnabled/NSAutoreleaseFreedObjectCheckEnabled enabled!");
        UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Developer Notice"
                                                        message:@"NSZombieEnabled is ON!\n* * *\nDisable BEFORE submitting!"
        [alert show];
        [alert release];
    if (kAllowCheats) {
        UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Developer Notice"
                                                        message:@"kAllowCheats is ON!\n* * *\nDisable BEFORE submitting!"
        [alert show];
        [alert release];

This first one I almost submitted an app, the second one; yup went live with my developer / cheat codes active. That wasn't a game center game so it wasn't a real problem but with all the hacking in Game Center games I suppose it could cause you extra work.

Tuesday, July 5, 2011

[iOS5] WiFi Sync Notice

Quick troubleshooting tip

Scenario: You have the iTunes beta install and are testing iOS 5, so you think "Hey let's try this WiFi sync out". Seems to work okay, cool. Except now your Xcode 4.0.2 goes bullsh!t and freezes up every three minutes!

Solution: Disable WiFi sync on ALL iOS 5 devices. :-(

Detail: With Xcode 4.0.2 all of a sudden my iOS 5 iPhone 4 would show up in organizer. Erm, yeah that's never good, it doesn't know what to do with a WiFi connection!

"Goodbye beach ball"

Sunday, July 3, 2011

[iOS5] Saved Files Alert!

Game, utility or whatever app you are developing. Know this... as of iOS 5; the USER will have full visibility into your apps Document folder. <- as of beta 2

I have already started saving my apps data to the /Library folder but in my recent game WARP gce, the "save" files are still in /Documents.

So on your iOS 5 test device go to Settings -> General -> Usage and select an app
We're are still under NDA so these images may go away.
Here's a couple of screenshots

Now let's swipe to delete - YIKES!

Now even in Hyper WARP I have a method to check that the file "is there" before it tries to load it but if you don't, well only YOU know what will happen ;-)

Oh and in the new multi-tasking order of things... you guessed it... you can of course DELETE these files WHILE your app is in the background, which again; only YOU know what problems that will cause.

What can we do / what should we do?
I think giving the user control over the device is certainly the right move and we as developers have always been able to choose which folder to save our files to. From all that I have seen and read the "/Library" folder is "our" folder and the normal user, even with iOS 5 will never "see" in there. So that is where all my "app" files are stored and starting with version 2.0 of WARP gce; so will the saved game files.

Finally time for another code snippet!

Here is my "move" method I'm using in WARP gce
playerDefaults is a pointer to [NSUserDefaults standardDefaults]
kDidMoveSavedFiles is a #define macro

-(void) moveSavedFilesToLibrary 
    // only run once kDidMoveSavedFiles
    if ([playerDefaults integerForKey:kDidMoveSavedFiles] < 1) {
        // documents folder
        NSArray *docFilePath = NSSearchPathForDirectoriesInDomains (NSDocumentDirectory, NSUserDomainMask, YES); 
        NSString *documentsDirectory = [docFilePath objectAtIndex: 0];
        // library folder
        NSArray *libFilePath = NSSearchPathForDirectoriesInDomains (NSLibraryDirectory, NSUserDomainMask, YES); 
        NSString *libraryDirectory = [libFilePath objectAtIndex: 0];
        // file manager
        NSError *error;
        NSFileManager *fileManager = [NSFileManager defaultManager];
        NSDirectoryEnumerator *direnum = [fileManager enumeratorAtPath:documentsDirectory];
        // while variables
        NSString *pname;
        int fileNum = 1;         // my saved game files start with 1.save, next would be 2.save
        int scoreNum = 0;        // my saved high-score files start with 0.scores for type 0 games, 1.scores for type 1 games, etc
        while ((pname = [direnum nextObject])) {
            // remove any .txt file <- you perhaps don't want to do this, I did :-)
            if ([[pname pathExtension] isEqualToString:@"txt"]) {
                [fileManager removeItemAtPath:[NSString stringWithFormat:@"%@/%@",documentsDirectory,pname] error:&error];
            if ([[pname pathExtension] isEqualToString:@"scores"]) {
                NSString *orgFileName = [NSString stringWithFormat:@"%@/%i.scores", documentsDirectory, scoreNum];
                NSString *newFileName = [NSString stringWithFormat:@"%@/%i.scores", libraryDirectory, scoreNum];
                // move file from Documents to Library
                [fileManager moveItemAtPath:orgFileName toPath:newFileName error:&error];
            if ([[pname pathExtension] isEqualToString:@"save"]) {
                NSString *orgFileName = [NSString stringWithFormat:@"%@/%i.save", documentsDirectory, fileNum];
                NSString *newFileName = [NSString stringWithFormat:@"%@/%i.save", libraryDirectory, fileNum];
                // move file from Documents to Library
                [fileManager moveItemAtPath:orgFileName toPath:newFileName error:&error];
                // check for matching log file
                NSString *orgLogName = [NSString stringWithFormat:@"%@/log%i.log", documentsDirectory, fileNum];
                if ([fileManager fileExistsAtPath:orgLogName]) {
                    NSString *newLogName = [NSString stringWithFormat:@"%@/log%i.log", libraryDirectory, fileNum];
                    [fileManager moveItemAtPath:orgLogName toPath:newLogName error:&error];
        // now update NSUserDefaults so this will never run again!
        [playerDefaults setInteger:2 forKey:kDidMoveSavedFiles];

Update: I'm working on adding analytics and guess what shows up...

Which of course is NOT a problem because those of you that are already using Google Analytics of course complied with the terms and notified your users... right?