iPhone app accepted!

Good news!

Our iPhone app was accepted into the iTunes App Store on the very first try!!!

I am far too tired to post details right now, I will modify this message after I recover a bit. For now, if you want to see it, fire up your iTunes, go into the App Store, and search for Basketball Statware.

CodeStock 2010, Day 1

Here were the sessions that I attended on day 1 (Friday, June 25, 2010) of the CodeStock 2010 developer’s conference in Knoxville, Tennessee:

  • Effective Interface Design by John Kellar
  • Effectively Using IntelliTrace by James Ashley
  • F# and Functional Programming by Chris Marinos
  • Visual Studio 2010 Architecture, Modeling, and Visualization by Jennifer Marsman
  • High Level Overview of Android Development by Roger Heim

All of the presenters I saw did nice jobs with their presentations. And to cap the night off, instead of sticking around the Bijou after the keynote was complete and getting loaded with all of the other developers, my coworker and I attended a local minor league baseball game.

iPhone app submitted to App Store

A momentous occasion, my company’s first iPhone app has been submitted to the App Store. Hopefully it will fly through the review process within minutes and be accepted the very first time. OK, maybe not.

There was one application uploading issue I ran into. The distribution code built just fine after a bit of tweaking, but upon uploading the .zip file to the iTunes Connect page, I got the following error:

The binary you uploaded was invalid. The application-identifier entitlement is not formatted correctly;
it should contain your 10-character App ID Seed, followed by a dot, followed by your bundle identifier.

As it turns out, I needed to make some changes to my Entitlements.plist file that did not appear to be mentioned or documented anywhere. Thanks to the TwoAppGuys and their blog entry “iOS4 and the wildcard” (link removed as it was broken) for the solution to this issue.

I will post again when I learn the ultimate fate of our app.

iPhone code signing issues

So I am trying to get a project started a while back into a state where it can be submitted to the app store, and of course it never goes smoothly. Today’s issue was a bunch of build and code signing errors that occurred when I tried to switch from using an older iPhone developer account to our current one, and added a new development device to the provisioning profile.

Here were the steps that I took to finally get it building and running on my development devices:

  • Regenerate the development certificate and provisioning profile after adding the new development device
  • Move the development certificate from the system area into the login area in Keychain Access
  • Clean out the old provisioning profiles in the Xcode organizer and from the device
  • Add the new provisioning profile to the device
  • Add the new provisioning profile to the provisioning profile section of the Organizer under iPhone Development
  • Change the code signing identity in the Project info Build tab and in the Target info Build tab

Once I did all these steps, it started to build and run on the device. Now I just can’t wait until I try to build for distribution and submit to the App Store. What could possibly go wrong?

2010 Central Ohio Day of .NET

A co-worker and I attended the Central Ohio Day of .NET on June 5, 2010. There was quite a bit of good content at the conference, which is a real tribute to the organizers, volunteers, and presenters.

The highlights of my day were sitting in on Matt Casto’s regular expressions talk, Phil Japikse’s M-V-VM primer, discussing the etymology of the MongoDB project with Sam Corder (I still say it was named such after the character in Blazing Saddles), Michael Eaton’s talk on WPF, and Parag Joshi’s demonstration of XNA/Windows Phone 7 game development.

UIButton can sing and dance, too!

Apparently, there is no way to take a UIButton and set it’s property to “selected” for the purposes of having the UI display the button with the background image as it appears in the UIControlStateSelected state. As a result, I decided that I would just track which of my series of UIButton controls was supposed to be selected, set the background image of all the buttons to nil, and set the UIControlStateNormal background image of the button to an image, and it seemed to work OK.

The code to do this, however, was kind of ugly, and my co-worker on this project pushed me to find a better way. The result is the ExpandoButton class that we created and added to the project. For this class (which of course inherits UIButton), I wanted to be able to initialize it to a particular background image, and I wanted to have a message I could send the button to have it switch back and forth from the unselected background image to the selected background image.

Here are the header and implementation files for the ExpandoButton object class:

//
//  ExpandoButton.h
//
 
#import <Foundation/Foundation.h>
 
@interface ExpandoButton : UIButton {
 
}
 
- (void)setSelectedImage:(BOOL)isSelected;
 
@end
//
//  ExpandoButton.m
//
 
#import "ExpandoButton.h"
 
@implementation ExpandoButton
 
- (id)initWithCoder:(NSCoder *)decoder
{
	if (self = [super initWithCoder:decoder]) {
		UIImage *image = [UIImage imageNamed:@"button_normal.png"];
		UIImage *stretchImage =
					[image stretchableImageWithLeftCapWidth:4.0 topCapHeight:4.0];
		[self setBackgroundImage:stretchImage forState:UIControlStateNormal];
		self.backgroundColor = [UIColor clearColor];
	}
 
	return self;
}
 
- (void)setSelectedImage:(BOOL)isSelected
{
	NSString *file;
 
	if (isSelected)
	{
		file = @"button_selected.png";
	}
	else 
	{
		file = @"button_normal.png";
	}
 
	UIImage *image = [UIImage imageNamed:file];
	UIImage *stretchImage =
				[image stretchableImageWithLeftCapWidth:4.0 topCapHeight:4.0];
	[self setBackgroundImage:stretchImage forState:UIControlStateNormal];
}
 
@end

A few notes here about the implementation file. You will of course need to add the stretchable unselected and selected button images to your project and change the names above, along with changing the cap width and height to your needs. Also, the initWithCoder part took us a few moments to figure out, it wasn’t working at first because we weren’t calling the super’s initWithCoder, just the regular old init. And finally, keep in mind that there is no error checking, optimization, or memory leak checking going on here.

To actually use the ExpandoButton, go into your Interface Builder, and from the Library window, select the Classes option, make sure the choice list right below is set to Library, and you should then be able to scroll the list and see an ExpandoButton, which looks just like a UIButton. Put that object on your view and set the size and title if you wish. VERY IMPORTANT: Make sure to set the button type of your ExpandoButton to Custom instead of the default of Rounded Rect.

Then, in your view controller header file, set up an IBOutlet with type of ExpandoButton (don’t forget the header or @class), along with the corresponding @property, and in the view controller implementation file, set up the @synthesize. After you save the files and build, go back into Interface Builder and set up the connection between the IBOutlet and the ExpandoButton on the view. (I hope you remembered where it was. When you set it to Custom and if you do not have any title text, the button sort of disappears.)

If everything is hooked up and working correctly, in your implementation file code, you can do something like this:

[myExpandoButton setSelectedImage:YES];

And the button should show up with the selected background image.

iPad 101: A Magical and Revolutionary Introduction (CIDUG meeting, May 25, 2010)

Justin Munger gave a presentation on some of the new features of the iPad and how to utilize them in the iPhone SDK at the Columbus iPhone Developer User Group on May 25, 2010. The topics he covered were the new modal dialogs, split views, gesture recognizers, and building a universal application for both iPhone and iPad.

A pearl of great price

If you are using Xcode for either Macintosh or iPhone development, please go to one of your implementation files in Xcode right now, hold down Control and Command and press the up arrow. Your life will be much better.

Easy SQLite for the iPhone

Most applications need data, shockingly enough. I started working on my iPhone application in earnest a couple of years ago, before the availability of CoreData on the iPhone OS. The project is just now picking up some momentum, but I was pretty sure that I wanted to stay with SQLite on the device.

As it turns out, even though using SQLite with Objective-C is not too terribly complicated, I thought that there could have been a better way, and so I found Gus Mueller’s FMDB for iPhone page. He wrote an Objective-C wrapper around the SQLite calls that seems to work very well.

In order to use FMDB, you will still need to follow along with steps in other SQLite tutorials (such as this one) and do things like setting up your database file in Firefox SQLite Manager, copying the database file from the resources to the local file system on the device if needed in applicationDidFinishLaunching, and opening the database using the FMDB open method.

Once you have your database open, you can do nice things such as this:

	FMResultSet *rs = [db executeQuery:@"select * from companies where state = ?",
					   stateToSearchFor, nil];
	while ([rs next])
	{
		NSLog(@"Name: %@", [rs stringForColumn:@"companyname"];
	}

Just make sure that, if you want to hold your database open throughout the life cycle of your application (instead of opening and closing the database each time you read, write, or delete a record), make sure to use retain on the object returned by the FMDB open command, as the code returns an autorelease object. Or you could of course just modify the FMDB code.

Nicely formatted data to debugger console for iPhone SDK

I was looking to be able see the data in one of my SQLite3 database tables on the iPhone in the console, but could not find a nice and easy way to do this. Here is my solution to the problem.

This routine is set up to take in an NSMutableArray corresponding to the rows of the tabular data, and each of the elements of the array is an NSMutableArray of the columnar data. All of the items in the column array need to be of type NSString, and the first element of the rows array should be a row of column headers. If you need other types or do not want to use a headers row, please feel free to modify the code as you see fit.

Here is the NSLogTable header and implementation files that I put together for this purpose:

//
//  NSLogTable.h
//
 
#import <Foundation/Foundation.h>
 
@interface NSLogTable : NSObject {
 
}
 
+ (void)dumpTable:(NSMutableArray *)rows;
 
@end
//
//  NSLogTable.m
//
 
#import "NSLogTable.h"
 
@implementation NSLogTable
 
+ (void)dumpTable:(NSMutableArray *)rows
{
	int idx = 0;
	NSMutableArray *rowData;
	int colWidths[100];
	NSString *s;
	NSMutableString *ms = [[NSMutableString alloc] init];
	BOOL firstRow = YES;
 
	for (int i = 0; i < 100; i++) colWidths[i] = 0;
 
	// get the maximum column widths
	for (id row in rows)
	{
		rowData = (NSMutableArray *)row;
		for (int i = 0; i < [rowData count]; i++)
		{
			s = [rowData objectAtIndex:i];
			if ([s length] > colWidths[i])
			{
				colWidths[i] = [s length];
			}
		}
	}
 
	for (id row in rows)
	{
		[ms setString:@""];
		if (firstRow)
		{
			[ms appendString:@"     "];
			firstRow = NO;
		} else {
			[ms appendFormat:@"%4d ", idx];
		}		
 
		rowData = (NSMutableArray *)row;
		for (int i = 0; i < [rowData count]; i++)
		{
			s = [rowData objectAtIndex:i];
			[ms appendString:[s stringByPaddingToLength:colWidths[i] withString:@" " startingAtIndex:0]];
			[ms appendString:@" "];
		}
 
		NSLog(@"%@", ms);
		idx++;
	}
}
 
@end

Once you build your rows array (make sure to import the NSLogTable.h file into your implementation file), you send it to the console like this, where theData is your NSMutableArray:

[NSLogTable dumpTable:theData];

As always, please keep in mind that there is minimal optimization and error checking going on in this code. If you have any suggestions, please let me know.