Showing posts with label Xcode 4. Show all posts
Showing posts with label Xcode 4. Show all posts

Thursday, July 14, 2011

Auto-Incrementing Build Numbers for Release Builds in Xcode

I use the wonderful Test Flight to distribute builds. One thing that Test Flight is a little picky about is build numbers. When you upload a build, it uses the build number to see if you're uploading a replacement or a new build. It will let you create a new build even if you don't remember to increment the build number, but it's an extra manual step, and then you end up with two builds with the same build number.

Because I'm forgetful, I wanted to automated this process. Basically, I wanted to increment the version short string any time we do an Archive and increment the bundle build ID any time we do a Release configuration build, but leave the version numbers alone on Debug builds.

Unfortunately, several of our projects are ones that we inherited or took over, so not every project uses the same version numbering scheme. How we increment 1.0b5 is different from how we increment 1.0.12, or a simple build number like 1058.

The way I deal with this is a Run Script Build Phase in my application's executable target that runs the following Ruby script (make sure you set the "shell" field to /usr/bin/ruby, and make sure the script is the last build phase in the application). Feel free to use this script if you wish and modify it to meet your needs. If you improve it, I'd be glad to incorporate improvements back into it. One item of note: the way that I differentiate between Archive builds and other Release configuration builds might be a bit fragile since I'm relying on an undocumented naming pattern in an environment variable.

Note: I'm aware of agvtool. I avoided it for two reasons. First, I wanted more control over the numbering schemes, and second, I tried using agvtool in a build script a few years back, but at that time, there were issues when you bumped the version numbers of a project that was currently open. Those issues may have been resolved, but I didn't want to fight that battle again.

def get_file_as_string(filename)
data = ''
f = File.open(filename, "r")
f.each_line do |line|
data += line
end
return data
end


def handle_alpha_beta(old_value, letter, infoplist, start_of_value, end_of_value)
parts = old_value.split(letter)
version_num = parts[0]
alpha_num = parts[1].to_i

alpha_num = alpha_num + 1
new_version = version_num.to_s + letter + alpha_num.to_s
print "Assigning new version: " + new_version + "\n"
new_key = "<string>#{new_version}</string>"

part_1 = infoplist[0, start_of_value - '<string>'.length];
part_2 = new_key
part_3 = infoplist[end_of_value + "</string>".length, infoplist.length - (end_of_value - start_of_value + (new_key.length - 1))]

new_info_plist = part_1 + part_2 + part_3
new_info_plist
end

def find_and_increment_version_number_with_key(key, infoplist)

start_of_key = infoplist.index(key)
start_of_value = infoplist.index("<string>", start_of_key) + "<string>".length
end_of_value = infoplist.index("</string>", start_of_value)
old_value = infoplist[start_of_value, end_of_value - start_of_value]

print "Old version for " + key + ": " + old_value + "\n"
print old_value.class.to_s + "\n"
old_value_int = old_value.to_i
print old_value_int.class.to_s + "\n"
if (old_value.index("a") != nil) # alpha
infoplist = handle_alpha_beta(old_value, "a", infoplist, start_of_value, end_of_value)
elsif (old_value.index("b") != nil) # beta
infoplist = handle_alpha_beta(old_value, "b", infoplist, start_of_value, end_of_value)
elsif (old_value.index(".") != nil) # release dot version
parts = old_value.split(".")
last_part = parts.last.to_i
last_part = last_part + 1
parts.delete(parts.last)

new_version = ""
first = true
parts.each do |one_part|
if (first)
first = false
else
new_version = new_version + "."
end
new_version = new_version + one_part
end
new_version = new_version.to_s + "." + last_part.to_s
print "New version: " + new_version.to_s + "\n"
new_key = "<string>#{new_version}</string>"
infoplist = "#{infoplist[0, start_of_value - '<string>'.length]}#{new_key}#{infoplist[end_of_value + '</string>'.length, infoplist.length - (end_of_value+1)]}"
elsif (old_value.to_i != nil) # straight integer build number
new_version = old_value.to_i + 1
print "New version: " + new_version.to_s + "\n"
new_key = "<string>#{new_version}</string>"

part_1 = infoplist[0, start_of_value - '<string>'.length]
part_2 = new_key
part_3 = infoplist[end_of_value + "</string>".length, infoplist.length - (end_of_value+1)]
infoplist = part_1 + part_2 + part_3
end
infoplist
end



config = ENV['CONFIGURATION'].upcase
config_build_dir = ENV['CONFIGURATION_BUILD_DIR']

archive_action = false
if (config_build_dir.include?("ArchiveIntermediates"))
archive_action = true
end

print "Archive: " + archive_action.to_s + "\n"


print config

if (config == "RELEASE")
print " incrementing build numbers\n"
project_dir = ENV['PROJECT_DIR']
infoplist_file = ENV['INFOPLIST_FILE']
plist_filename = "#{project_dir}/#{infoplist_file}"

infoplist = get_file_as_string(plist_filename)
infoplist = find_and_increment_version_number_with_key("CFBundleVersion", infoplist)
if (archive_action)
infoplist = find_and_increment_version_number_with_key("CFBundleShortVersionString", infoplist)
end
File.open(plist_filename, 'w') {|f| f.write(infoplist) }
else
print " not incrementing build numbers"
end

Wednesday, May 4, 2011

Empty OpenGL ES Application Xcode 4 Template

Jacques De Schepper sent in an updated version of my old Empty OpenGL ES Application Template, updated to use Xcode 4's completely revamped templating mechanism. You can download the new Xcode 4 version of the template right here. I haven't had a chance yet to test this out, but once I do, I'll also add it to GitHub.Thanks, Jacques!

Sunday, February 13, 2011

Xcode 4 Icons

Well, now that Xcode 4 is GM, it has the same icon as Xcode 3.25. Ordinarily, this wouldn't be an issue, since a GM release means you can throw out the old one and use it full-time.

Only, you may not be able to with all of your projects. Xcode 4 GM has a few issues that make it hard to go full-time with it, including a linker error that can only be worked around by turning some level of code optimization, a change that makes it hard to debug. A few of these problems impact projects I'm working on, so as a result, I have to grudgingly use Xcode 3.25 for some tasks.

Despite a small handful of problems, though, Xcode 4 is where I want to be whenever possible. Having multiple identical icons in your Dock can be a bit of a pain.

Unfortunately, I didn't save the old Xcode 4 installers. If I had, I would have just gone and stolen the old preview icon and continued using that. Since I didn't, I did the same thing I did for beta iOS releases and made a customized version of the app icon for Xcode 4. If you want to use it, you can download it here. To install, you just copy the .icns file into Xcode 4's app bundle, replacing the existing one. And, no, replacing the icon file with a new one doesn't cause problems with Xcode 4 due to code signing, though I feared it might.

This is what it looks like:

Xcode

Thursday, September 16, 2010

Global Symbolic Breakpoints in Xcode 4

It wasn't obvious to me how to set a global symbolic breakpoint in Xcode 4. I stumbled across the answer today. Because Xcode 4 is still under NDA, I can't post this here, so instead, I wrote it up on Apple's Dev Forums (Dev Center login with beta access required).

Wednesday, June 30, 2010

Modern ABI & No More @Synthesize

I haven't talked about one of my favorite new features of Xcode 4 and LLVM, because I wasn't sure of the NDA status. However, the cat's out of the bag now, so I feel like it's safe to mention. The iPhone Simulator in Xcode 4 now uses the Modern ABI (yet it's still correctly uses 32-bit datatypes!), so we can use synthesized instance variables in our iPhone applications and use the simulator. On top of that, if you choose to use LLVM instead of GCC, one of the (many) benefits you'll get is that @synthesize declaration is now assumed. If you don't need to specify anything on the @synthesize declaration (like a different setter or getter name) and your property isn't @dynamic, you can justleave it off. This feature isn't turned on by default (yet), but see Pilky's blog post for instructions on enabling this feature.

Less code to write? Boo-yeah! Count me in!

I believe (though I haven't confirmed this yet) that you can write code this way and still target older releases back to 3.0. If anyone knows otherwise, please let me know and I'll spread the word, though.