When you start writing an iOS application, you are typically focused on getting the app built and shipped. That means writing the code, maybe some unit tests to make sure your logic works as expected, and a script or two that builds and runs your app in a Continuous Integration system.
Then, with any luck, your application will gain traction and you will acquire lots of customers. Lots and lots and lots of customers.
Maybe, like us, your customers will need your app to be branded with their own artwork. So, you write a small Bash script that can replace your artwork with theirs when needed.
Then, some of your customers need special one-off features that the rest of your customers don’t want. These customers are critical to your company, so you write the feature and manage it via a special properties file combined with another small Bash script that enables or disables that feature.
After a while, your customer base will grow, your team will grow like ours has, and the small Bash scripts will grow in size and complexity. Soon, other team members won’t understand how it all fits together, and one small change can break other things in unexpected ways, and everyone will cringe whenever something needs to change in the build.
At Appian, our build system had fallen into this sad state. Our confidence in our system was at an all time low and our stress levels were off the charts after several incidents required us to scramble to fix build-system changes in order to re-enable features that we had inadvertently broken.
After taking a step back, I decided something had to be done; our build system needed to be fixed. I brought it up with the team and we considered building our own system using Python, Swift, or Ruby, in order to get the benefits of a higher level language. We decided against that because we didn’t want to re-invent the wheel and it would have taken a very long time. That is when we starting searching for something better. Thankfully, one of my co-workers found fastlane.
Right away, I knew fastlane was special; it provided support for installing different versions of Xcode; it promised automatic uploads to TestFlight; it made the output from automated tests looks so clean and simple; and it was written in Ruby, a higher level language that would allow us to build a modular, understandable, and testable build system.
I used Appian’s IndieTime (similar to Google’s 20% time) to convert our entire build system to use fastlane and the result was pure joy.
We can now rely on getting the correct result for all of our different build options. Our developers find it easier to understand and make changes to the build system, and complex project changes are much easier and simpler to implement.
With the new system, we have reaped many benefits. These three stand out the most:
1. A Self-Testing Build System with RSpec
When we were using Bash scripts for our build system, it was very difficult to ensure that all the configurations were still being built correctly. When we added a new feature, or modified the system, it was very easy to make a mistake and introduce a bug that sometimes would not show up for months until we needed to provide clients with their custom builds. When such bugs did appear it could take a long time to determine the root cause and then fix it correctly.
With the Ruby based fastlane toolset, and RSpec, I was able to put the entire system under unit tests.
Now, whenever we add a new capability that involves changes to the build system, we add unit tests. Our Continuous Integration system runs over 160 tests to ensure that we build the different iOS configurations correctly.
2. Simplified Logic Using fastlane
With Bash, in order to not break our existing build scripts and to save time, developers would duplicate the Bash script and tweak the copy. Our iOS codebase ended up with many almost identical scripts and obscure, tangled, and undecipherable code.
When transitioning to fastlane, one of our developers suggested that every build should be treated as a custom build. So now, every build goes through the same customization path:
- our App Store app,
- our in-house app,
- our HockeyApp,
- and custom branded apps for our customers.
This way we ensure that all our builds work or we find out very quickly if we broke something.
As an example of how this helps us: Apple just added a new requirement that we need to package the iTunes artwork with the app bundle. To make that change, we added only 6 lines to support all our different builds:
Our Product Manager couldn’t believe how easy it was to support this feature for all of the build configurations that we provide.
3. Easy, Yet Sophisticated, Project Manipulation with Xcodeproj
While some of the unique features that our clients need in our application are easily managed with a runtime check against a custom property (aka feature toggle), other features require more fundamental changes to the project. Sometimes these changes are incompatible with our other clients needs.
For example, some of our clients require our app to be integrated with external devices, and other clients require our app to be integrated with Mobile Application Management (MAM) SDKs.
Some of these project changes, if left in, would cause our application to be rejected from Apple’s App Store, or would not allow us to use features from the latest iOS APIs.
Before we would use Bash to run sedcalls to search and replace text inside the Xcode project file. This was messy and error prone.
Now we use the amazing power of the xcodeproj Ruby Gem in our fastlane code. It allows us to dynamically and elegantly edit the Xcode project file before building to:
- remove Swift 3 files from the Compile Build Phase.
- alter Run Script Build Phases when building with Xcode 7.
- remove Notification Extension Targets from the dependencies and turn off Push Notifications.
Advice On Getting Started
If you are excited to build an elegant and powerful build system, here is what I suggest you do:
Bash is fine to use when your needs are tiny, but once you start adding logic or you need to deploy to Apple’s App Store or TestFlight, you really should advance to great build tools such as fastlane and Ruby Gems such as xcodeproj, blackberry_mam, etc.
Moving from Bash as the basis of your build system to an advanced build system not only makes your developers happier, but it will make your Product Manager happier, and most importantly, it will help you keep your customers happy 💰.
Consider how you can improve your build system:
- What wasted redundancies do you have?
- Where does your build system waste time and resources?
- What breaks the most, and what do you or your fellow developers dread doing in the build system?
Chances are, you’ll be able to solve those problems with fastlane.
Imagine the best build system and dare to build it!
Rescue your mobile builds from madness using fastlane was originally published in Appian Engineering on Medium, where people are continuing the conversation by highlighting and responding to this story.