Wednesday, 19 June 2013

Automagically versioning of iOS projects for a team using git

In our shop, we are building iOS projects as a team. We use git as our distributed version control system.

Challenge:
Display an automatic build number within the app, and have it updated properly across all developers.

Solution:
We display the current build information inside a simple dialog using code like this:


NSDictionary *appInfo = [[NSBundle mainBundle] infoDictionary];
NSString *versionStr = [NSString stringWithFormat:@"%@ (Build %@)",
                                [appInfo objectForKey:@"CFBundleShortVersionString"],
                                [appInfo objectForKey:@"CFBundleVersion"]];
        
UILabel *versionLabel = [[UILabel alloc] initWithFrame:versionLabelFrame];
versionLabel.font = [[UIFont contentFont] fontWithSize:10.0];
versionLabel.text = [NSString stringWithFormat:@"Version: %@", versionStr];


Which gives us this nice output: Version: 1.0 (Build 311)

Making this happen can be as simple as inserting the proper value into the CFBundleVersion field of the apps' Info.plist file.

That task is pretty simple if you're working solo. With multiple members contributing however, it becomes tricky. Throw in the curve ball that is git's distributed versioning, and it gets really complicated.

Some online searching lead us to try following these very old articles.


They were successful in what they attempted, but their use cases are slightly different that ours. In those examples both authors are working on solo projects, and want to retain more control over the incrementing of the build numbers.

On our team, we want the build number to increase with every single commit to the repo. This is because each build is automatically extracted from git and pushed out to our internal testers using TestFlight. The users can accurately report to us issues based on a build number.


Challenge:
Handling a single version number across multiple developers using a distributed version control system.



Solution:
Macros with the Jenkins Continuous Integration server

Jenkins CI logo

Configure Jenkins using the Xcode Plugin.
Jenkins uses environment variables to track and expose each build it does on our behalf. In this way, each build knows the current build number.

This plugins will call the avgtool to bump the current build number and then stick the result into the main target's Info.plist.

In our case, we use the brilliant, and affordable build service from Hosted-CI.com. Their build server already has the necessary plugins installed. For us, it was as simple as adding "${BUILD_NUMBER}" to the Technical Version field of the Xcode plugin.


Now the entire process is as easy as a git checkin. Every single time any of the developers on the team pushes a build to git, we get Jenkins to bump the build number, and inject that into Info.plist before building the project. (As a bonus, it runs our unit tests and proceeds or halts based on those results.)
Each successful build is then sent to TestFlight for distribution to the distribution list of our choosing: Developer, Internal or External testers. This last decision is made manually, as we don't want to flood our testing teams with several builds each day.






No comments:

Post a Comment