Our BlogTips, Tricks, and Thoughts from Cerebral Gardens

Party Doodles Featured on TUAW Origin Stories

Our latest app, Party Doodles was recently featured on TUAW's Origins Stories. The web series takes a closer look at the reasons why developers create their apps. Our very own Dave sat down with TUAW's Victor Agreda, Jr. to discuss how Party Doodles came to be and why it is such a unique app. Check it out!

What a Week! WWDC 2012 Edition

This was my first WWDC, but it certainly won't be my last. It was a great experience and I'm going to try and share some of the things I learned over the last week. Nothing that's covered by the NDA of course.

1) It was great to finally meet some of the big wigs in the community. Drinking beer with Jeff LaMarche and the other MartianCraft guys. Hanging out with the Empirical Development guys that I've been working with for most of the last year was awesome. Getting to pitch Party Doodles to Eli Hodapp (of Touch Arcade) and Victor Agreda, Jr of (TUAW) in person was amazing. I'm sure it helped that Apple basically used Party Doodles as an example of how to do an AirPlay game correctly.

2) Probably the biggest shock to my system was the amount of walking involved. As someone who normally sits at a desk for 12+ hours a day, it was a major change to walk back and forth from my hotel 2 or 3 times each day. Why 2 or 3 times you ask, depending on whether or not I took my laptop to the sessions and wanted to drop it off at my hotel before dinner/socializing etc, or based on meetings with various people I had scheduled between sessions.

3) In most cases, you do not need to take your laptop with you to the sessions or labs. I had a completely incorrect assumption of what the labs were. Labs should be considered more like Q&A sessions with Apple engineers. They are not planned tutorials or anything scripted. They're just a chance to ask a question, sometimes with someone who may have helped build the system you have a question about. The only time having your laptop with you is probably if you need to show an engineer your code during your Q&A (lab) session.

4) For the labs, my own experience was pretty dismal in this regard. I had a few questions to ask about various topics, and each time, the engineer(s) I was talking to had no more information to provide on the issues. That being said, I heard of some people that had much more successful visits to the labs.

5) The actual sessions where amazing. Some covered brand new information about iOS 6 or Mountain Lion, while others covered older information that you might have missed. Sometimes you see something presented that's been available for a while that you just hadn't seen and you think "this will save me hours". When the session videos are released, make sure you watch as many as you can. Even if you think you already know about a topic. There are always extra little tips that are invaluable.

6) When you attend a session in person, please use some common decency and follow these four rules:

  1. When sitting down, move to the center of the row, don't 'end cap' the row by sitting in the first seat. Most sessions fill the entire room and when everyone has to fill in rows by jumping over a person sitting in the first seat, it's pretty annoying.
  2. Wait until the speaker has finished talking before running out to the next session. We all have to get to the next session at the same time, give the speaker the respect they deserve by letting them finish.
  3. Do not use a MiFi device. They jam the provided WiFi and in some cases prevented even the presenters from being able to demonstrate what they had planned.
  4. Take your trash with you. If you bring in a drink, lunch etc, just take the garbage with you when you leave and drop it in the garbage bin or recycling etc. I think they teach this in kindergarden but it appears some people missed that day.

7) Related to #2 above, the choice of hotel is important. The closer the better (or at least the less walking you have to do). But there are other issues. I only have experience with the one I stayed at this year, Parc 55 Wyndham, but I'm pretty sure I won't be staying there again next year. The room was nice, clean etc, most of the staff were nice and helpful. My issues with the hotel were

  1. the network is awful. Wifi or wired, it wasn't strong enough to keep iChat connections alive. And they charge $15/day ($50/week).
  2. the included breakfast only includes pastries, you can add eggs and bacon for $25!
  3. the elevators are extremely slow, taking up to 10 minutes to go up and down.
  4. the TVs are locked down and prevent you from adding your own input, no connecting Apple TV or your laptop for example. That made testing some changes to Party Doodles impossible.

8) Since I'm Canadian and our roaming fees are insane, I wanted to pick up a local SIM card in order to be able to use data whenever I needed. I have an unlocked phone so it should have been easy. Eventually I went AT&T, $50 for unlimited voice and text, and $25 for 1G that they said wouldn't work on an iPhone and that they wouldn't refund the cost if I couldn't get it to work. After putting in the SIM card, it took all of about 30 seconds to switch the APN using the site: http://www.unlockit.co.nz/. The AT&T network has been great the whole week (Keynote excluded, but nothing was working there).

9) J.J. Abrams. Wow. He was a guest speaker for the Friday lunch session. And boy was his talk amazing. For one, he was by far the most entertaining speaker of the week, granted his content makes it easier, blowing up stuff is more exciting by itself than NSManagedObjects being accessed by the wrong NSManagedObjectContext. But his way of presenting was great, it almost felt like it was just me and J.J. in the room and he was telling me stories from his life. It was very interesting to hear how certain ideas/shows came to be due to other events in his life, in much the same way we move from app to app where the first app may inspire the idea for the second app. I'd love to go into more detail here, but it seems even this talk is covered by the NDA. J.J., if you're reading this (maybe Google Alerts brought you here), I just want to say thanks for the awesome and inspiring presentation.

10) One last point. Since it was my first WWDC, I wasn't sure when I should be here, so booked my flight for Saturday to Sunday. Getting here on Saturday worked out well, gave me some time to get to know the area and meet up with people for drinks etc. But next year I'll leave Friday night or Saturday morning. There wasn't much happening on Saturday or Sunday as most people have already left.

I'd say WWDC (I'm not yet cool enough to be able to call it "dubdub") was a great success this year. I can't wait for next WWDC 2013! It'll sell out super fast again next year, so be prepared...

If you haven't already, please download my free game Party Doodles, like us on Facebook, and if you like to hear me ramble, follow me on Twitter.

Introducing Party Doodles, and the Lessons It Has to Share

Last Wednesday we released our newest game, Party Doodles. A unique picture guessing game that uses AirPlay mirroring to create a party game experience unseen before now on the App Store. It has been in the works since AirPlay mirroring was announced (yes, almost a year from concept to release).

I believe, and app reviewers (here, here, and here) seem to agree, that Party Doodles is the first of it's kind on the App Store. The tag line for the game is 'Made for iPad, designed for Apple TV' because while you can play without Apple TV, the game is really meant to be played on the Apple TV.

The easiest way to describe the game, is to compare it to Pictionary. A group of friends break into two teams, each person takes a turn drawing clues (on the iPad) while their teammates try and guess what it is (watching on the big screen TV) before time is up. If time runs out, the other team can steal the points. I added some strategy to the game by having 3 difficulty levels baked into each turn, the person doodling can pick an easy topic to win 1 point, a medium topic for 2 points, or a hard topic for 3 points. If you're behind the other team it gives you a chance to catch up, or if you're winning, a chance to get even further ahead!

The game really is a lot of fun, and I'm not just saying that cause I made it. I had people actually come over to my house and ask to play it. And it's not uncommon for players to break out into uncontrollable laughter.

Anyway, enough plugging the game, please download it and give it a try. Did I mention it's free?

On to what lessons the game has for us, as developers....

First, it's important to know that progress on the game was sometimes slow as I was working full time at a normal job when I first started working on the game. Even when I went full time indie, client projects usually take precedence and consume the majority of my time. I recently looked back at my source commits and noticed several months where I hadn't committed a single line.

A lot of things happened over the course of the development of the game. One of which was that Draw Something came out and became a massive hit. There was a definite 'Oh shit' moment at that point. But it didn't take long to come to terms with the fact that the games are significantly different. I never intended Party Doodles to have mass market appeal anyway. It's definitely for a niche market since the requirements to play properly are high; you need:

  1. an iPad
  2. an Apple TV
  3. actual friends in the same room1

The goals for Party Doodles were to create a unique experience doing something that no other app on the store was doing yet (I expect more will come along shortly), to try the freemium model out, to create a fun game that people can really enjoy playing together, and to get a little attention for the game itself since it is the first of it's kind on the App Store, not an easy feat nowadays.

I've definitely learned a lot while developing this game and plan to share a bunch of tips/tools and code over the next few blog posts. Today's tip is a short one though (since I used up so much space detailing the game in the first place).

When releasing an app with In-App purchases, make sure you test the purchase functionality with the live app from the App Store as soon as it's available. (Use a promo code to download the app after it's approved and before it's released if need be). There are some slight differences between the Production IAP system and the sandbox we can get to test with before submission. In my case, I added receipt verification as suggested by Apple (using PHP on the server, with code I'll likely share in a later post) and the verification process would fail on production requests only due to a minor difference in data sent back from Apple. That caused the first few purchases to be rejected. Not a good user experience at all! Even Apple's reviewers test the app in the sandbox and thus missed my production only bug.

Fortunately I was able to fix it fast since it was just server side code and not in-app code that needed to be changed.

Ok, lots more to come in future articles so stay tuned. In the meantime, please download the game, and like us on Facebook.

1As a french review (Google Translation) of the game pointed out, you also need a TV which they said makes my free game very expensive. I hadn't considered that someone would add the TV to the cost of my game.

Dev Tips & Tricks

Today I'm going to cover some useful tips and tricks. These are presented in no particular order, and are pretty much unrelated to each other. Hopefully you'll find some, or all of them useful.

1. Regarding the upcoming iPad 2

Reports are starting to surface that the next version of the iPad will support a retina type display. Apple will no doubt repeat what they did with the iPhone 4 and double the resolution (4 times the number of pixels). This makes it easy to support old apps on the new device by employing pixel doubling.

But, you can start preparing for this now! For every iPad image you use, include a higher resolution (double sized) image with the @2x suffix. And for icons, include a 144x144 (double 72x72) icon. I've included a 144x144 icon in all my iPad apps since the iPhone 4 was announced, betting on Apple doubling the iPad resolution. It's cheap to do, and if the predictions are wrong, there's no harm in having an unused icon.

As a sub tip, you should include the following sizes for your icons: 144x144, 114x114, 72x72, 58x58, 57x57, 50x50, 29x29

2. self.var vs var

In your classes, when use the following syntax:

DWClass.h

@interface DWClass : NSObject {
    NSObject *myObject;
}

@property (nonatomic, retain) NSObject *myObject;

DWClass.m

@synthesize myObject;

You're telling the compiler that your new class DWClass will have a property called myObject, and that it should create setMyObject (setter) and myObject (getter) methods to access that property. And that those methods should handle your retain/release cycles for you. Any other objects that need to interact with your myObject property, will do so by like this:

dwClass.myObject // (assuming dwClass is an instance of DWClass)

And this will actually call the appropriate setter/getter for the myObject property, which in turn interacts with the myObject instance variable of the dwClass.

Inside the DWClass however, you can access the property like this:

self.myObject

And the same setter/getter methods are used.

Inside the DWClass, you can also access the myObject instance variable directly just by referencing it. DO NOT do this. If you do that, you're not using the setter/getter methods, which means you're not automatically handling the retain/release calls. This is a surefire way to create hard to find bugs in your code. Plus, there are other problems with doing this. If down the road, you need to do something special whenever that property is accessed or updated, so you ditch the @synthesize and create your own setter/getter methods, you're now going to miss even more than the retain/release calls, you're going to miss whatever else you've added.

This entire tip also applies to non-object properties. Even if you're using a standard int as a property, always use the self.variable syntax to access it. It's just good practice and will save you headaches down the road.

3. Keeping your secrets, secret

Often when you're accessing other services, Twitter, Dropbox, your own servers etc, you may need to store passwords, API keys, etc in your code. It's dead easy to just have a constant like this: @"MySuperSecretKey" and be done with it. If you do that though, you may as well post the secret on your web site for all to see. Since, it's trivial for a bad guy to extract your secret from the compiled code they download from the App Store after your release. This is a bad thing. In the case of Twitter for example, some spammer could put your secret key into a rogue app that spam blasts users. Every one of those tweets will say it was sent by your app, and your legit app will be blocked pretty quickly. Your users will be locked out until you submit a fix and have it approved by Apple, say goodbye to two weeks of sales, not to mention, all the bad reviews you'll receive for selling a non-functioning app.

So, keep your secrets secret, use some encryption inside of your app to encrypt your keys etc. It doesn't have to be complex as the bad guy usually just looks for low hanging fruit (unless they are specifically targeting you). You can use one of the many encryption libraries available, or even roll your own if you're so inclined.

4. Ensure the App Store knows you support multiple languages when you do so

This tip comes from a mistake I made in version 1.0 of the Cruze app. The app supported English and French from the get go, but it was done by detecting the language of the user via code and loading the correct set of files for the primary language. This worked great and was way less work than using Apple's recommended method of localization for every nib etc.

One problem though, once the app was released to the store, the only supported language listed in iTunes was English. Because I used my own language detection iTunes Connect didn't detect French. I fixed this in 1.5 by using Apple's localization on a small dummy text file. One that I don't actually use for anything in the app, but that is enough to trigger the language detection tools Apple uses.

Update: see Ovogame's comment below for an even better way to fix this issue.

5. AppName_Prefix.pch

There's a file in your XCode project named AppName_Prefix.pch (where AppName, is your app's name). This file is included at the top of every source file in your project. It's a great place for you to store any constants you need across your app.

6. A Better NSLog()

A common method of debugging is to add NSLog() calls throughout your code. The messages are echoed to the screen as the code runs and you can see what's happening, giving you hints as to what's causing bugs. When you're finished however, and you want to do your final build, all of those NSLog() calls remain in your final build and all of those strings are available for anyone looking through your binaries to see. Who knows what secrets you might disclose.

Instead of using NSLog(), I use DebugLog(). This is a tweak of a function I’ve seen others use, based on the answer here: http://stackoverflow.com/questions/300673/is-it-true-that-one-should-not-use-nslog-on-production-code.

Add this to your AppName_Prefix.pch file:

#ifndef DebugLog
#ifdef DEBUG
#define DebugLog( s, ... ) NSLog( @"<%p %@:(%d)> %@", self, \
	[[NSString stringWithUTF8String:__FUNCTION__] lastPathComponent], __LINE__, \
	[NSString stringWithFormat:(s), ##__VA_ARGS__] )
#else
#define DebugLog( s, ... )
#endif // DEBUG
#endif // DebugLog

Now, replace all of your NSLog() debugging statements with DebugLog(), and define DEBUG in your debug configuration (sub tip 2: go to your Project Info, Debug configuration, search for Preprocessor Macros, add DEBUG).

After this, use DebugLog() all you like, and the strings are skipped over in your Release and Distribution builds. You also have the added bonus of having the function name and line number included with all debug statements now, making it clear what's generating the messages.

7. *.dSYM files

Whenever you build your app, XCode will output the *.app files, as well as a *.dSYM file. For Debug and Release builds, you can just toss/ignore the *.dSYM file. But for your distribution build, that you're going to submit to the App Store, make sure you keep the .dSYM file. You'll need this later, to analyze crash reports. I'll go into more detail on this in a later post, just know you need to keep the files. For the impatient, you can read more on this here: http://www.anoshkin.net/blog/2008/09/09/iphone-crash-logs/

8. Push Notification Certificates

If you have an app that uses push notifications, you need to generate a certificate with Apple, one of the first steps, is to create a Certificate Request file (CertificateSigningRequest.certSigningRequest), that you send to Apple. Keep this file. When your certificate expires, you'll need to request a new one. You can reuse the same Certificate Request file and skip the first few steps.

9. [object release]; object = nil;

With NSObjects, when you're done with them, you call release to reduce the retain count and let the system know you no longer need it. When that retain count hits 0, the system free's the object and releases the memory allocated back for use later.

It is common practice, to set the object to nil after you call release. This prevents a possible crash later, if you attempt to call a method on an object that has been freed. That's because if you try to call a method on a nil object, the system just ignores it, no error, all is good (or is it). If you don't set the object to nil, then it will still point to the memory address that was allocated for that object. There's a chance that object is still there (if something else was using it and increased the retain count for example). In that case, calling a method on that released object, will still work. But if that memory had actually been released calling the method would cause a crash.

This is why developers often set the object to nil, to prevent that crash in the case of a bug. But, this doesn't fix the bug, it just hides it from you. So, I subscribe to the second school of thought you shouldn't set the object to nil. If you have a bug, let the app crash while you're developing and you'll be able to find and fix that bug. When you no longer have any crashes, you'll know you're (closer to being) bug free, and that you haven't just masked your bugs.

Three’s Company: Multiple XCode Versions Living Together Peacefully

Apple is making fantastic progress with iOS. They continue to release firmware updates and betas at a frequent pace, and with each new release, developers must download and install a new version of XCode compatible with the new firmware. Managing the various versions and their associated firmwares can be a challenge. In this article I'm going to give you a couple of tips that will hopefully help.

It's not uncommon for developers to have multiple versions of XCode installed on the same system in order to support development/testing across a wide range of devices and firmware versions. XCode by default installs in /Developer, and it is common practice to install the latest beta version in /DeveloperBeta. Those testing XCode4 know its default install folder is /XCode4. Personally I found this to be messy and inadequate.

My preferred setup now is to create a directory /XCode and install each version of XCode underneath using the version info as it's base folder name. Currently this looks like this:

/XCode/3.2.3_4.0.2
/XCode/3.2.4_4.1b3
/XCode/xcode4_dp2

When installing XCode in this fashion, you’ll find that in some cases, you won’t be able to install/debug a build on one of your devices because of a mismatch in firmware versions. For example, the XCode 4 Developer Preview 2 was released before firmwares 4.0.2 for iPhone and 3.2.2 for iPad. So if you’ve updated your devices to those firmwares, you’re now unable to use XCode 4 to directly install or debug.

There is a simple fix however. You just need to create symlinks from one version of XCode to another. Assuming you’re using a layout similar to what I’ve detailed above. If you look under /XCode/xcode4_dp2/Platforms/iPhoneOS.platform/DeviceSupport you’ll see folders for each version of iOS that you can install/debug on. 3.2.2 and 4.0.2 are obviously missing. If you’ve installed the latest version of XCode 3 with support for those versions, you can make XCode 4 work with them.

In terminal, issue the following commands to create the required symlinks:

ln -s /XCode/3.2.3_4.0.2/Platforms/iPhoneOS.platform/DeviceSupport/4.0.2 \
	/XCode/xcode4_dp2/Platforms/iPhoneOS.platform/DeviceSupport
ln -s /XCode/3.2.3_4.0.2/Platforms/iPhoneOS.platform/DeviceSupport/3.2.2 \
	/XCode/xcode4_dp2/Platforms/iPhoneOS.platform/DeviceSupport

Similarly, If you’re using the 4.1 beta 3 firmware on your iPhone, you’ll need XCode 3.2.4_4.1b3 installed, and can then enable support for that firmware version in XCode4 also:

ln -s /XCode/3.2.4_4.1b3/Platforms/iPhoneOS.platform/DeviceSupport/4.1\ \(8B5097d\) \
	/XCode/xcode4_dp2/Platforms/iPhoneOS.platform/DeviceSupport

If you have your XCode versions installed under a different directory structure (/Develper, /DeveloperBeta, and /XCode4 etc), you’ll just need to tweak the above lines to point to the correct folders.

A similar trick can be used to allow you to use a base SDK of 3.1.3 or earlier, which is no longer included in the latest versions of XCode. Just create links under the /XCode/Platforms/iPhoneOS.platform/Developer/SDKs to an older XCode that does include support for the SDK you need.

If you have any tips to improve upon this, see an error in what I’ve described, or otherwise have anything else to contribute, please let me know.