Debugging EXC_BAD_ACCESS in Xcode 4.2

The program receives the EXC_BAD_ACCESS signal when a message is sent to an object that has been deallocated. With the environment variable NSZombieEnabled set to YES, objects are never deallocated, but instead kept in memory as zombie objects. When a message is sent to a zombie object, execution stops at the line that would normally cause the EXC_BAD_ACCESS signal.

In Xcode 4.2, you could set environment variables by editing scheme, select the “Run XYZ.app” stage in the left panel, then select the “Arguments” tab on the right.

It’s important that you disable NSZombieEnabled once you’re done, or your application would use a tremendous amount of memory and eventually would crash.

Read more about it on CocoaDev.

Easy Analytics for Mobile Apps

Link

I recently updated Scribblr to use the EasyTracker library. It enables you to track your UIViewControllers like down to the Activity level “with almost no coding required on your part.” Basically, all you really need to do to integrate with EasyTracker is to update the provided plist file and change all your controller classes to inherit from TrackedUIViewController. Depending on how organized your project is, it could be done within 5 minutes! Now you have no excuse not to integrate with Google Analytics :)

CCMenu Grid Layout

It’s been a while since my last post. I’ve been busy with my next app, trying to make the Christmas release deadline.

Today I’d like to share a reusable solution for laying out CCMenuItems in a grid, similar to 7 Little Words or Angry Birds:

This is a slightly modified version of @gaminghorror‘s method in this forum post, with some differences:

  • Automatically updates contentSize; this is necessary if you use it with CCMenuAdvanced
  • Lays out menu items based on their content size, but assumes all menu items to be the same size.
  • Menu items are laid out bottom-to-top, so the first menu item is positioned with higher y value than the second menu item, and so on.

CCMenu+Layout.h:

#import "cocos2d.h"
 
@interface CCMenu (Layout)
 
- (void)alignItemsInGridWithPadding:(CGPoint)padding columns:(NSInteger)columns;
 
@end

CCMenu+Layout.m:

#import "CCMenu+Layout.h"
 
@implementation CCMenu (Layout)
 
- (void)alignItemsInGridWithPadding:(CGPoint)padding columns:(NSInteger)columns
{
    CCMenuItem *item = [children_ objectAtIndex:0];
    CGFloat contentWidth = item.contentSize.width * item.scaleX;
    CGFloat contentHeight = item.contentSize.height * item.scaleY;
 
    // set content size
    NSInteger count = children_.count;
    NSInteger numRows = (count + columns - 1) / columns;
    NSInteger numCols = MIN(count, columns);
    CGFloat height = contentHeight * numRows + padding.y * (numRows - 1);
    CGFloat width = contentWidth * numCols + padding.x * (numCols - 1);
    [self setContentSize:CGSizeMake(width, height)];
 
    // layout menu items
    NSInteger row = 0;
    NSInteger col = 0;
    CCARRAY_FOREACH(children_, item) {
        CGFloat x = (contentWidth + padding.x) * col + contentWidth * 0.5f;
        CGFloat y = height - (contentHeight + padding.y) * row - contentHeight * 0.5f;
        [item setPosition:ccp(x, y)];
 
        if(++col == columns) {
            col = 0;
            row++;
        }
    }
}
 
@end

I hope you’ll find this useful. Please follow me, @tonyngosays, on Twitter!

Scrolling CCNode in Cocos2d

For my next app, I want to be able to scroll the content (children) of my CCNode. Cocos2d offers no easy way to this. Instead, I found a few third-party solutions:

While these work, they just don’t quite behave the way I expect. Here are my requirements:

  1. I want the scrolling behavior to behave exactly like iOS’s native-style scrolling (like UIScrollView). If I scroll past the top/bottom edge, the content should bounce back. If I quickly swipe my finger, the content should scroll at the same initial velocity and decelerate over time.
  2. I want a simple solution, preferably a subclass of CCNode, which I could instantiate and add to it the scrollable content.

So I decided to roll my own solution. Here’s a preview:

I only had an hour to work on it, so I only got it working with static content. CCMenu will scroll, but itemForTouch: will return the wrong menu item. I’ll revisit this later after I get back from Las Vegas. If you know a better solution, please leave a comment below.

You may also download the source code to see what I did. It’s just an experimentation, so there is no documentation. You have been warned :) Please follow me on Twitter, @tonyngosays.

Twitter Integration Tutorial

One of the exciting new features in iOS 5 is Twitter integration. Photos, YouTube, Safari, and Maps come fully integrated. With most iOS 5 users jumping on the Twitter bandwagon (Twitter daily signups tripled, according to Twitter’s CEO), allowing your users to tweet about your app could help put it in front of more people. In this iDevBlogADay post, I want to write a tutorial for integrating your app with Twitter.

Getting Started

Launch Xcode and create a new project. Select the Single View Application template as your starting point.

Leave project options as default. Make sure iPhone is selected for Device Family, and Use Storyboards and Use Automatic Reference Counting (ARC) are checked.

Add Twitter Framework

To take advantage of the Twitter framework, you must first add the it to your project:

Click on the project in the Navigator, select the project’s target, and switch to Build Phases. Then click on the arrow button next to Link Binary With Libraries. You should see a list of frameworks already included in the project. Now click on the + button and select Twitter.framework to add it to this list. The framework is designated as Required by default. Click on the text Required besides Twitter.framework and select Optional. Designating the Twitter framework as optional is necessary for supporting users running earlier versions of iOS.

Modify ViewController

Open ViewController.h and change its content to:

#import <UIKit/UIKit.h>
 
@interface ViewController : UIViewController
 
- (IBAction)tweetButtonPressed:(id)sender;
 
@end

We have just declared a new method tweetButtonPressed:. Open ViewController.m and add an import line at the top:

#import "Twitter/Twitter.h"

Then add the implementation for our new method above didReceiveMemoryWarning:

- (IBAction)tweetButtonPressed:(id)sender
{
    Class tweeterClass = NSClassFromString(@"TWTweetComposeViewController");
 
    if(tweeterClass != nil) {   // check for Twitter integration
 
        // check Twitter accessibility and at least one account is setup
        if([TWTweetComposeViewController canSendTweet]) {
            TWTweetComposeViewController *tweetViewController = [[TWTweetComposeViewController alloc] init];
 
            tweetViewController.completionHandler = ^(TWTweetComposeViewControllerResult result) {
                if(result == TWTweetComposeViewControllerResultDone) {
                    // the user finished composing a tweet
                } else if(result == TWTweetComposeViewControllerResultCancelled) {
                    // the user cancelled composing a tweet
                }
                [self dismissViewControllerAnimated:YES completion:nil];
            };
 
            [self presentViewController:tweetViewController animated:YES completion:nil];
        } else {
            // Twitter is not accessible or the user has not setup an account
        }
    } else {
        // no Twitter integration; default to third-party Twitter framework
    }
}

This method isn’t as complicated as it looks. Let’s go through it so we know what’s going on:

Class tweeterClass = NSClassFromString(@"TWTweetComposeViewController");
 
if(tweeterClass != nil) {   // check for Twitter integration
}

Do you remember that we designated the Twitter framework as optional? Backward compatibility is important since many users are not yet running iOS 5. But to do so, we must check for Twitter integration first. If the user is running an earlier iOS version, tweeterClass would be nil, and the method would safely return without crashing. Let’s continue onto the next if-block:

// check Twitter accessibility and at least one account is setup
if([TWTweetComposeViewController canSendTweet]) {
}

canSendTweet is a class method that returns YES if Twitter is reachable and at least one account is setup in Settings.app. Although you could just let iOS tell the users that they must setup an account first, checking canSendTweet allows us to customize the notice and enhance the users’ experience. Now we are ready to allocate and initialize the tweet compose sheet:

TWTweetComposeViewController *tweetViewController = [[TWTweetComposeViewController alloc] init];
 
// set initial text
[tweetViewController setInitialText:@"Check out tonyngo.net!"];
 
// add image
[tweetViewController addImage:[UIImage imageNamed:@"tweetImage.png"]];
 
// setup completion handler
tweetViewController.completionHandler = ^(TWTweetComposeViewControllerResult result) {
    if(result == TWTweetComposeViewControllerResultDone) {
        // the user finished composing a tweet
    } else if(result == TWTweetComposeViewControllerResultCancelled) {
        // the user cancelled composing a tweet
    }
    [self dismissViewControllerAnimated:YES completion:nil];
};
 
// present view controller
[self presentViewController:tweetViewController animated:YES completion:nil];

First we allocate the view controller. Notice anything unusual? There is no autorelease or release anywhere in this method. That’s because we’re taking advantage of iOS 5′s ARC mentioned previously. Next we set some initial text. If the text contains a URL, it would be shortened automatically. Then we add an image in the next line. You no longer need to worry about image hosts like Twitpic; the image will be uploaded directly to and hosted by Twitter (pic.twitter.com). After that, we setup the completionHandler. It’s a block defined as follow:

typedef void (^TWTweetComposeViewControllerCompletionHandler)(TWTweetComposeViewControllerResult result);

Blocks are only available in iOS 4, which means you cannot support earlier iOS versions. result is either TWTweetComposeViewControllerResultDone, if the user finished composing a tweet, or TWTweetComposeViewControllerResultCancelled, if the user cancelled composing a tweet. The view controller does not remove itself from the view stack, so we dismiss it with dismissViewControllerAnimated:completion. The parameter completion is new in iOS 5. It allows you to execute arbitrary code after the controller has been removed from view. Use nil if you don’t need it, like we did here. And finally, we present the view controller to the user.

Add Tweet Button

Open MainStoryboard.storyboard and drag a button control into the blank view controller. Double click on this button to rename its label to “Tweet.”

Now ctrl-drag the button to the View Controller below. A small popup menu will show up after you release the mouse button. Select tweetButtonPressed:. Save, and you’re done! Build and run the app and tweet something :)

Source Code

Source code may be downloaded here. If you find this tutorial useful, please tweet and +1 it below. Thank you :)

Introducing Scribblr

My next app, Scribblr, is now available for download from the App Store!

Apple approved it sooner than I had anticipated. I submitted it last Friday night, Oct 14, and expected it to be approved no earlier than this Friday, Oct 21. To my surprise, today I received an email from Apple telling me that my app was ready for sale. The timing couldn’t have been better! This means we’ll be able to submit an update and get it approved by Halloween.

Scribblr is a simple game in which you prove how well you know your Facebook friends by drawing about them. It posts your drawings onto Facebook to be judged for accuracy by your friends. Sometimes you learn things about your friends you never knew :) Check it out!

In-App Settings with InAppSettingsKit

According to Apple’s “iOS Human Interface Guidelines” section “De-emphasize Settings”, the preferred way to provide settings for your app is through the Settings.app. If your app contains frequently accessed preferences, however, presenting them directly inside the application may be a better option. Personally, I feel that a hybrid solution, providing access to the settings page in both places, is ideal.

In this iDevBlogADay post, I want to discuss an easy way to add in-app application preference, using the iOS Settings Bundle and InAppSettingsKit. InAppSettingsKit is an open source framework authored by Luc Vandal and Ortwin Gentz. It allows developers to add in-app settings to their iPhone apps by mimicking Settings.app’s behavior.

I’m currently on vacation, so apologies for the shortness of this post. Leave a comment if you need me to discuss anything in detail. Let’s begin…

Creating and Modifying the Settings Bundle

First you must create a settings page in Settings.app. This step is very straight forward. The instructions are outlined in Apple’s “iOS Application Programming Guide” section “Implementing Application Preferences”.

Your app will need to reconfigure itself during startup as well as during runtime. It’s recommended that you do all reconfiguration inside a -reconfigure method, so there won’t be any code duplication.

Creating In-App Settings

Add InAppSettingsKit’s source files to your Xcode project:

  1. Clone the InAppSettingsKit GitHub respository.
  2. In Xcode’s Navigator, right click on your project, choose Add File to “YourProjectName.”
  3. Inside the InAppSettingsKit project, you will see two directories: InAppSettingsKit and InAppSettingsKitSampleApp; select the former, click on the checkbox for Copy items into destination group’s folder (if needed), and then click Add.

Create a delegate that conforms to the IASKSettingsDelegate protocol. In the current version, -settingsViewControllerDidEnd: is the only required method. Within this method is where the app should reconfigure itself for changed settings. Fortunately, we had conveniently created a method for reconfiguring the app, so we’ll just call it.

- (void)settingsViewControllerDidEnd:(IASKAppSettingsViewController*)sender {
	[self dismissModalViewControllerAnimated:YES];
	[self reconfigure];
}

Finally, allocate an instance of IASKAppSettingsViewController, initialize it with your delegate and push it onto the stack.

You should now have a settings page in Settings.app as well as in your app. I hope you found this post useful.

UIWebView and baseJS

As an iOS developer with a background in web development, I’ve decided to write a tutorial on UIWebView as first post to iDevBlogADay :)

Imagine this, John Doe, aka Mr. ImBetterThanYou, challenges you to create an iOS view with the following requirements, in the shortest amount of time possible. Given a percentage value, display a progress bar and the string “Congratulation! You’ve completed X% of this tutorial!” The text must wrap naturally with the varying digits of the percentage. In addition, the word “congratulation” must be bolded, and the percentage must be green with larger font size.

Assuming you are up to the challenge, what would you do?

Back when I began iOS development, my immediate approach would have been to create several UILabel components, one for each font style, and position them adjacent to each other, based on where the previous one’s text end. As for the progress bar, I would use the UIProgressBar component and style it to look like the image above.

This is the slow approach. Not only does it take a very long time, but also the code is specific to the style. If John comes back and tells me that the word “you” should be underlined, I’ll have to create another UILabel and add it to my layout. A faster approach, hinted by this post’s title, is to use the UIWebView component. Here is a preview of the completed example:

I think it’s unnecessary to walk you through the code (download link below). I will, however, provide some tips to customizing your own UIWebView:

Javascript framework

Using a Javascript framework is not necessary, but it could make coding easier. If you decide to use one, check out baseJS. It’s a lightweight framework written specifically for the iOS platform. There is no cross-platform nor cross-browser code, so your page will load much faster than other frameworks.

Disable scrolling

Add the following lines to your document load event handler:

document.ontouchmove = function(event) {
	event.preventDefault();
};

This setups an event handler for ontouchmove and prevent its default behaviors.

Disable bounces

Disables the bouncing rubberband effect when the page is scrolled beyond its bounds.

// disable bounces
for (id subview in webView_.subviews)
	if ([[subview class] isSubclassOfClass: [UIScrollView class]])
		((UIScrollView *)subview).bounces = NO;

Execute Javascript in UIWebView

Call stringByEvaluatingJavaScriptFromString with your script as the parameter. Here’s an example taken from the example project:

- (void)setPercent:(NSInteger)percent
{
	NSString *javascript = [NSString stringWithFormat:@"ExampleApp.setPercent(%i)", percent];
	[webView_ stringByEvaluatingJavaScriptFromString:javascript];
}

Call Objective-C from Javascript

Check out this post about how to properly call Objective-C from Javascript. Kyle Newsome had also posted an excellent tutorial demonstrating this technique.

Transparent UIWebView

Set the opaque and backgroundColor properties of UIWebView:

webView.opaque = NO;
webView.backgroundColor = [UIColor clearColor];

And set the background-color property of the body element:

body {
	background-color: transparent;
}

Disable selection

Add the following lines to your CSS style:

* {
    -webkit-touch-callout: none;
    // Disable selection/Copy of UIWebView
    -webkit-user-select: none;
}

Disable all user interactions

If you want to disable all user interactions, including selection, copy, paste and clicks, simply set the userInteractionEnabled property to NO.

Source code

Source code may be downloaded here. If you find this tutorial helpful, please +1 this page below :)

Don’t Lose Sight of Your Target Audience

Who is your target audience?

Every game designers, including amateur ones, such as myself, knows that defining your target audience one of the most important steps in game design.

When we began designing Button Smasher, we did not forget this. Kevin and I decided to design it for casual gamers, and we considered them with every design decision made. We even conducted surveys to ensure those with little to no gaming experience could simply pick up the game and begin playing immediately.

Then Button Smasher v2.0 came around. With the addition of power-ups, we faced the challenge of balancing them. We were mostly concerned about one power-up in particular: the time bonus. While it is active, every successive button smash rewards the player with one bonus second. This means an experienced player could potentially play indefinitely. To prevent this situation, we needed determine a new duration for the power-up, such that the total number of seconds gained from it can never be more than amount of time needed to obtain it.

We decided the scale it against the skill level of our best player (out of roughly 1500 scores submitted to OpenFeignt). We shortened the power-up’s duration to 5 seconds. This was a huge mistake (I realized this thanks to MochiBits’s insightful post about their experience).

His highscore was far above average. Though we achieved our goal, we also rendered the power-up completely useless to our casual gamers. Imagine Mario’s star power-up’s duration being only 5 seconds; you get the idea :) .

We learned a very important lesson here: don’t lose sight of your target audience!

iOS Facebook Like Button

20 hours ago, I tweeted:

wonders why there aren’t many Facebook like buttons in iOS apps. Are they ineffective?

I found my answer the hard way today. So I’m currently working on the next app, which we have decided to name Scribblr. Since Scribblr was already setup to connect to Facebook (hint: it’s a social app), we thought it would be a good idea to add an easily accessible Facebook like button, right into the menu view. Just now, I spent an hour trying implement this requirement. When all was done, I fired up the iOS Simulator, and to my surprise, I got the following error:

{
	"error":{
		"message":"(#3) Application does not have the capability to make this API call.",
		"type":"OAuthException"
	}
}

A bit of searching brought me to this page, which tells me that you can’t like a page on behalf of a user. How unfortunate; I suppose Facebook added this limitation to combat spam.