skip navigation

Here you will find ideas and code straight from the Software Development Team at SportsEngine. Our focus is on building great software products for the world of youth and amateur sports. We are fortunate to be able to combine our love of sports with our passion for writing code.

The SportsEngine application originated in 2006 as a single Ruby on Rails 1.2 application. Today the SportsEngine Platform is composed of more than 20 applications built on Rails and Node.js, forming a service oriented architecture that is poised to scale for the future.

About Us
Home

Continuous Integration for iOS Projects

10/25/2013, 3:30pm CDT
By Mark Larsen

Use the objective-ci RubyGem to get up and running quickly with continuous integration for iOS projects.

Requirements

  • Ruby
  • Jenkins on an OSX machine.

TL;DR

Follow the examples at the objective-ci README to quickly integrate your iOS project into Jenkins.

Why should you care about continuous integration?

You've got a pretty good app, and a team of sharp developers working on it. Isn't that enough? Think again.

Throughout the development life-cycle, there are many evils that will pop up and attempt to thwart you and your team's efforts to produce a quality app and codebase. Requirements will change, deadlines will haunt, members will leave the team, new members will replace them, knowledge will get lost in the transfer. Expect the unexpected.

With continuous integration, it's easy to ensure that tests are always passing, and that code that meets your team's standards is being committed. Hunt and tame the unexpected nature of the chaos monkey; don't let it run amok in the code.

Enough blabber, and onto the main show. With objective-ci, I hope to make continuous integration with iOS so stupid-simple that you won't even need convincing to start implementing continuous integration into your workflow today. If you'd like to read more on the benefits of continuous integration, Martin Fowler's article provides a great overview.

Enter objective-ci

objective-ci is an open-source RubyGem. Its mission is to take all of the useful binaries and knowledge regarding Objective-C code metrics and test results, put them into one place, and provide a common interface for interacting with them through Ruby.

Why Ruby?

Ruby has a lot going for it -- it's an incredibly slick scripting language with a great community, strengthened by the portability of RubyGems. Most importantly, CocoaPods is already dependent on Ruby and RubyGems (you are using CocoaPods, right?), so you should be able to add objective-ci to your Gemfile faster than you can say bundle.

Great -- what exactly does it do?

objective-ci currently provides a respectable selection of metrics, all viewable through Jenkins. Pick and choose as you please.

Test results

This may have been the most tiresome to set up -- unfortunately, it was also the most desired metric. There's nothing like that feel-good moment where you commit some code and the tests still pass. Ready to ship? You know it!

Xcode 4 posed immediate issues, throwing errors when we ran the tests with xcodebuild. Many online had found work-arounds where one could do some shell hacking and overriding of Xcode scripts to fix things -- this was less than ideal, and quite frankly, scary. Luckily, Xcode 5 was just hitting the proverbial shelf.

Xcode 5 made life immediately easy. It still took a lot of guess-work to find the right arguments to xcodebuild, but no hacking was required. The magic command ended up being

$ xcodebuild test -workspace FooApp.xcworkspace ONLY_ACTIVE_ARCH=NO \
-scheme FooApp -configuration Test -destination \ 
name=iPad -destination-timeout=10`

We're not quite in the clear, yet. We've got results, but they're in OCUnit format -- unsurprisingly, not a format that many support. The RubyGem OCUnit2JUnit came to the rescue, allowing us to convert the results to the Jenkins-friendly and all-powerful JUnit format with a simple pipe.

$ xcodebuild  test -workspace FooApp.xcworkspace ONLY_ACTIVE_ARCH=NO \
-scheme FooApp -configuration Test -destination name=iPad \
-destination-timeout=10 >&1 | ocunit2junit`

Jenkins can now parse the JUnit like a beach read, and will even fail the build if there are any failing tests.

Lint

Lint tools are great for sniffing out general code smells. The OCLint 0.8 dev documentation is helpful, but let's go over the quick and dirty here. Running OCLint in an iOS project is a three-step process.

Note, 0.8 dev is used specifically because 0.7 threw spooky errors on the second step.

$ xcodebuild ONLY_ACTIVE_ARCH=NO -workspace FooApp.xcworkspace \
-scheme FooApp -configuration Release clean build | tee xcodebuild.log`

The preceding command doesn't deviate a whole lot from the one used to run the tests above -- the main difference being that we want to clean and build for release. tee simply saves the output to a file, xcodebuild.log, while also showing us the output.

$ oclint-xcodebuild

This will utilize the xcodebuild.log generated above to gather the compiler options for each option in the project, saving them to compile_commands.json.

$ oclint-json-compilation-database -e Pods -e \ 
FooAppTests -- -report-type=pmd -o=lint.xml`

Finally, this command employs the compile_commands.json file to lint each source file and return the resulting warnings to a file of our choosing (in this case, lint.xml). We decide to exclude Pods and FooAppTests, because we're not interested in seeing warnings in external libraries we don't have control over (Pods) or in our tests (we could see value in generating these, but this ultimately was a personal and maybe even temporary choice). The report-type is also set to pmd, which is necessary in order to integrate with the Jenkins PMD plugin that we'll be using later.

Copy-paste detection

You've finally tracked down that hard-to-find bug, and you've got a fix brewing up for it in your brain! Wait -- what's this? Mr. White wrote some code, and then Mr. Pink and Blue came along and unknowingly (or lazily) put the same code into a different spot? You've got to apply this fix across n many files in n * x many places? You continue to flip a table.

Copy and pasting is easily one of my personal least favorite evils that can bedevil a codebase. Let's put an end to this injustice.

PMD CPD is the tool we seek. Plus, the "P" in the logo is a gun -- that's cute. A gentleman by the name of Josh Kennedy was nice enough to compile an Objective-C language defintion for PMD, and his blog post is concrete in demonstrating how to glue the two together. Needless to say, objective-ci does this glueing for you by being packaged with the necessary JAR files, no assembly required.

There was an interesting, and unfortunate, discovery when setting up PMD, however. The JAR seems to provide no command-line option for excluding files from the CPD analysis. This simply wouldn't do -- while it makes us sad to know that some of the CocoaPods we're using might have copy and paste naughtiness in them, we cannot fret over what we cannot change.

objective-ci handles exclusion for PMD CPD after-the-fact by opening up the resulting xml file and removing entries we've asked to be excluded.

Lines of source code

Let's finish things off with a slightly lighter, yet still important, metric. SLOCCount is a dandy little tool that will report how many lines of code your project is, what language that code is in, and how it's distributed across your files. This can be particularly helpful in finding bloated areas of code, or just patting yourself and your teammates on the back for how much code you've written.

$ sloccount --duplicates --wide --details . | grep -v -e "Pods" > line-count.sc

  • duplicates tells sloccount to count files that have the same exact content (PMD CPD will get'ya ...)
  • wide makes output pretty, so that the Jenkins SLOCCount can parse it
  • details specifies that we want information for each source file.

The grep done at the end simply omits files matching the pattern given -- in this case, the always-ignored CocoaPods. Do you think they're getting lonely?

Code signing

It's important to keep in mind that, although we've wrapped this all in a fun RubyGem and we're working from the command-line, we still have to play by Xcode's rules. Make sure to import any code signing certificates into Xcode on the continuous integration server, and to keep the provisioning profiles up to date. And, most importantly, the first time your code signing certificates are used with objective-ci, a dialog will appear asking you to allow xcodebuild to access the keychain. Click always allow

Jenkins

At Sport Ngin, we've been using Jenkins to automate our Ruby builds for some time now. Our Node apps then joined the Jenkins family, and when we finally decided to cook something up for our iOS projects, it was great to be able to have the builds live alongside our other apps in Jenkins. Additionally, we had a spare Mac Mini sitting around the office just begging to get in on the programming fun.

If you're looking for something a little less DIY, and have got some spare change, it may be worth investigating a hosted solution. Xcode 5 also released with Bots, and while there are some posts hinting at roughness there, we admittedly did not explore it enough to form an opinion on its usefulness.

Setting up Jenkins is a bit too out of the scope of this post, but there are many great tutorials available out there for doing it. Stay tuned to the Coding in the Crease blog for future posts on how we've set up Jenkins and smoothly integrated it into GitHub with Janky. For information regarding which Jenkins plugins we use with objective-ci, check out the README.

Pull Requests

objective-ci's main goal is to focus all of Objective-C CI knowledge and tools into one easy-to-use package. Familiar with Mac OSX programming and interested in using it? Know how to generate test coverage reports? Want to simply make what's there better and easier to use? Feel free to open a pull request!

Tag(s): Home  iOS