Skip to content

Recent Articles

17
Oct

Automatic Build Numbers for iOS Apps Using Xcode

UPDATE: Now also updating the build version in the dSYM file.

There are many articles that discuss how to automate build numbers in Xcode. However, some are misleading for iOS apps. Many are quite long. I wanted to find a short, simple way to do this.

Background

On the target Summary tab your project settings, Xcode lets you set a Version and a Build. The version is what we are all familiar with, such as 5.0 and 5.1.1. The build is what often appears in parentheses after the version, as in 5.0 (134) and 5.1.1 (147).

Apple doesn’t seem to care what you use for Version (aka CFBundleShortVersionString), but Build (aka CFBundleVersion) must be a monotonically increasing string, comprised of one or more period-separated integers. Thus Apple will reject your update if you go from 1.1 (10) to 1.2 (0). The version is ignored and, alas, zero is clearly less than ten.

The part about integers is important too because Apple will also reject your update if you go from 1.01 (1.01) to 1.1 (1.1). The period is not a decimal place. Both (1.01) and (1.1) are interpreted as “the integer one followed by the integer one”. We saw this logic in action when OS X went from version 10.4.9 to 10.4.10.

Problem

If you do this wrong, you’ll see this error when you upload your app update:

This bundle is invalid. The key CFBundleVersion in the Info.plist file must contain a higher version than that of the previously uploaded version.

To get your monotonically increasing period-separated integer build number, you could just use the app version, like 5.1.1 (5.1.1). But I think it’s better to use a build number that can help identify the code from which that version of your app was built. Both Git and Mercurial include the ability to count commits, which is perfect—always increasing and helpful in identifying the code.

If you modify the Info.plist file in your project folder during the build process, you’ll probably need to commit the change to your code repository. This extra commit, while not harmful, is unnecessary. Instead, you can modify the Info.plist file in the app package after the build process is finished. The file will be in a binary format, but the PListBuddy tool can handle it.

Solution

Here is the script you need for Mercurial. This script also adds the bundle version to the dSYM file, which is necessary for correctly symbolicating your crash logs. It’s also required for several distribution mechanisms including TestFlight and HockeyApp.

ver = `/usr/local/bin/hg id -n`.strip
puts "Build number is #{ver}"
filepath = "#{ENV['BUILT_PRODUCTS_DIR']}/#{ENV['INFOPLIST_PATH']}"
puts "Updating #{filepath}"
`/usr/libexec/PlistBuddy -c "Set :CFBundleVersion #{ver}" "#{filepath}"`
filepath = "#{ENV['DWARF_DSYM_FOLDER_PATH']}/#{ENV['DWARF_DSYM_FILE_NAME']}/Contents/Info.plist"
puts "Updating dSYM at #{filepath}"
`/usr/libexec/PlistBuddy -c "Set :CFBundleVersion #{ver}" "#{filepath}"`

The first line counts the number of commits in your local repository. It’s safe to use as long as your builds are done on the same machine. Repositories on other computers may have different commit counts. For Git, you can count your commits using a similar command.

Steps

  1. Select your project in the Project Navigator
  2. Select your target
  3. Select the Build Phases tab
  4. Choose “Add Build Phase”
  5. Select “Add Run Script”
  6. Change the Shell to “/usr/bin/ruby”
  7. Copy and paste the script

Within your app, you can grab the version and build number with this code:

NSDictionary *appInfo = [[NSBundle mainBundle] infoDictionary];
NSString *appVersion = [NSString stringWithFormat:@"%@ (%@)", appInfo[@"CFBundleShortVersionString"], appInfo[@"CFBundleVersion"]];

This will give you a string like 2.2 (134) that you can display in your app. I’m using the new object subscripting syntax in Objective-C, but it’s not too hard to switch back to using objectForKey:.

10
Oct

Information on the Internet Lives Forever

I bought a mattress a while back and a bad experience with one of the products I purchased. The company handled the return much more gracefully that I was expecting. As I wrote up my experiences with the whole mattress buying experience, I mentioned my mixed experience and moved on. I didn’t think much more about it.

Until the owner of the store called me. We were both a little tense. I was worried about threats and lawsuits, and he was worried I’d be a jerk. He was losing some business from people who’d read my article. Yay for blogger justice! But his concern was correct because information lives forever on the Internet.

He could have fixed the problem, or switched to a new supplier, or discontinued that product, or even gone out of business, and my article would remain. I’m glad he called. He was nice, and I’m glad to have the opportunity to keep my article current.

Information on the Internet lives forever. Unless you want to find something cool you know you read several years ago, of course. Then it’s probably gone forever. I just try to keep up.

11
Sep

My Best Stuff

You may have noticed the new My Best Stuff section on the side. The articles there either continue to be the most viewed or have generated the best reaction from people who know me. I didn’t think much about it when I was calling it Popular or Recently Popular. Calling it “my best” makes me nervous.

It made me realize that those articles probably do represent my best writings, or at least my most successful. And I wonder if they’re actually any good. No real way to find out, except by forging ahead anyway.

So please don’t hesitate to let me know if I’m wrong about what to include in the list or if you have any comments or suggestions. You can follow me on Twitter here or email me .

I’ll update the list on occasion as I discover which articles resonate with people the best.

4
Sep

My Current Project

A few months ago, two friends asked if I’d be interested in working on a book project with them. Jed is a successful artist and illustrator. Noelle is a brilliant writer and educational designer. They’ve worked together on several projects and wanted a programmer to help design a book app. I said yes before they stopped talking. :)

Here is one of Jed’s early designs:

Story app mock up

It’s a Choose Your Own Adventure style story app, except that the story is cohesive and targeted at a young adult audience. In the CYOA books, the endings were often very different from each other. For example, the plot might change from bank robbers to aliens depending on which door you went through. Also you died a lot.

In our story app, you nudge the main character in the direction you want her to go even as the story unfolds around her. The book remembers the choices you make and adjusts the text and artwork as you go along. Noelle is writing the same story more than 20 times and loving it. She’s been planning this story since high school.

A website is coming soon. We’re close to finishing the programming, art and writing for chapter one. Noelle is wrapping up chapter two in a few weeks.

Jed recently completed a successful Kickstarter project for traditional hand-made Japanese woodblock prints of video game characters. It’s kinda cool, so check it out if you’re interested.

3
Aug

3 Tips on Auto Synthesized Properties

Ha ha! You fool! You fell victim to one of the classic blunders, the most famous of which is “never get involved in a land war in Asia.” But only slightly less well-known is this: “Never have an auto-synthesized property and an instance variable with the same name when death is on the line!”

—Vizzini

At work, my team decided to drop support for iOS 4. Most of our customers have upgraded to iOS 5.1. Surprisingly, very few are still using iOS 5.0. I guess over-the-air upgrades are working really well for people.

Our app is old. In some places we still checked if the user had upgraded to iOS 3 yet. :) My team spent the week on cleanup and paying down technical debt. My job was to:

  1. Convert to using object literals for arrays, dictionaries and numbers.
  2. Remove @synthesize calls.
  3. Switch all of our instance variables to properties.

I love shortening and removing code without losing functionality. The first two tasks went fairly quickly. But I…uh…learned a lot while tackling number three.

You can declare an instance variable in an @interface block — either the public interface in the .h file or the private interface in the .m file. You can also declare one in the @implementation block in the .m file. Or all three at once.

You can mix instance variables, properties and auto-synthesized properties. But auto-synthesized properties are so much nicer: less code, less maintenance, less boilerplate typing. Here are 3 tips to help you use properties more effectively.

1. Deal With Read-Only Properties

An auto-synthesized property generates an instance variable with the same name as the property except prefixed with an underscore. This is great because it’s clear whether you’re using the property (self.myprop) or the instance variable (_myprop). Using the bare name (myprop) doesn’t even compile, so you limit the confusion and bugs caused by accidental mistypes.

Auto-synthesized properties create their own getter and setter methods (avoiding some unnecessary typing). You can implement your own getter and/or setter, in which case those methods are not automatically generated. The instance variable (_myprop) is still synthesized.

However, if your property is readonly AND you implement your own getter method, then an instance variable is not synthesized. I suspect this is to allow for a named property that is dynamically calculated. In any case, if you would like an instance variable, you can force it to be synthesized by either:

  1. Using @synthesize myprop = _myprop; in your @implementation, or
  2. Redeclaring the property as readwrite in your private @interface

The second option works like inheritance in that you can redeclare a property as less-restrictive but not the other way. Redeclaring a public readwrite property as privately readonly isn’t possible, though why you’d want to do that remains a mystery.

2. Avoid Instance Variables

It is a Bad Thing™ to have both an auto-synthesized property and an instance variable declared with the same name.

  1. self.myprop = 1 uses the auto-synthesized instance variable
  2. myprop = 1 uses the declared instance variable
  3. _myprop = 1 uses the auto-synthesized instance variable

Since you end up with two instance variables, when you probably intended to have only one, doing this can cause lots of unexpected bugs and problems. Better to avoid declaring instance variables for your synthesized properties.

It is a Worse Thing™ to have two auto-synthesized properties of the same name with one prefixed with an underscore.

    @property myprop;
    @property _myprop;

This causes problems because self._myprop is different than _myprop when you really should have used __myprop, making you start to wonder about your own sanity. This doesn’t seem like it would happen much, but it can if you are importing several .h files, one of which uses underscores in its property names.

So don’t do that. :)

3. Use Instance Variables Correctly

In most cases, it’s best to stick with using the property (self.myprop) in your code. However, it’s important to use the instance variable (_myprop) in two cases:

  1. In your init: methods to avoid possible side-effects if you implement your own setter later, and
  2. In your custom getter and setter methods to avoid infinite recursion (always nice).

Auto-synthesized properties are great. Not quite as awesome as ARC, but close.

6
Jul

Bacon Candy

Picture of finished bacon candy

A delicious recipe for bacon candy from my friend Randy Hall:

  • 1 pound thick-cut bacon
  • 1 cup brown sugar
  • 1 teaspoon cayenne (optional)
  • 2 teaspoons favorite BBQ dry rub (optional)

Preheat the oven to 375 degrees. Mix brown sugar, cayenne, and BBQ rub together in a medium bowl. Place sugar mixture a gallon Ziploc bag. Add bacon and toss. Line a baking sheet with foil, and place a wire rack in the baking sheet. Lay the strips of bacon on the rack. Pat any remaining sugar mixture on the bacon. Put the baking sheet on the top rack of the oven and bake until crisp, about 20-25 minutes, turning once halfway through (re-coat with any additional sugar when turning if you want even more candy coating). When the bacon reaches desired crispiness, remove from the oven to a serving dish and let cool slightly before serving.

It’s a super simple recipe. The only real gotcha is finding the fine line between nice crispy bacon and black burnt sugary mess. Check it often at the end. It is delicious.