Back to all posts

Life in the fastlane: an Android developer's review

David JonesDevelopment

Towards the end of January, 2017, Google acquired the developer platform, Fabric, the idea being that the combined business would help mobile teams build better apps, better understand their users and ultimately grow their companies. Included was a set of developer tools called fastlane, created through an open source project, which were already being used by thousands of development teams to automate both the building and the release of iPhone and Android apps.

For iOS, the development environment is XCode which manages everything from creating an iOS app to signing and deploying to the App Store or TestFlight. There are already a lot of build tools to improve the developer workflow on iOS, and a lot of this is down to XCode exposing functionality via the terminal. On Android however, things are slightly different, with the undisputed king being Gradle.

Most Android developers won't need to interact much with Gradle; it will just sit there running scripts until changes need to be made to external dependencies, app properties or signing configuration. But its features don't stop there; it can also be set up to provide functionality such as build variants (e.g. demo and full app versions) and automatic versioning. With a plugin system, it can even automatically deploy to services like HockeyApp and Google Play. 

Why then, would Google absorb a project that, at first glance, would seem to duplicate vast swathes of functionality already built into developers' workflow? The question becomes even more pertinent when we consider it only has two tools available for Android, compared to more than 10 for iOS, and just about half the actions. But, aside from Google's incessant need to create two of everything, fastlane does provide some benefits for mobile app developers looking to automate build processes.  

Among the biggest is portability between applications - and this is the reason I wanted to implement fastlane into my own projects. On Gradle, one is forced to set up scripts individually for each project. Hence, when writing a fancy Gradle method to increment a version code or upload to Google Play, this needs to be copied and customised for each app created. 

With fastlane, on the other hand, it's possible to keep the same script - known as Fastfile - and share it across all projects, both on iOS and Android. If there are any major differences between the apps' set-up, this can be accounted for easily by modifying environment variables, or creating a new lane (a set of instructions) within the same Fastfile.

Another benefit of fastlane - as the name itself suggests - is speed. Indeed, a quick Google search reveals many complaints about Gradle build times. And, while fastlane doesn't resolve the problem completely, moving some work onto it certainly leads to improvements. To illustrate the point, I used to use Gradle tasks to automatically increment my app's version code and version name - something that could take up to 15 seconds each time. Running similar scripts in fastlane, however, takes no time at all, as can be seen in the Fastlane Summary image below.


[object Object]

Finally, another big benefit of fastlane is that anyone can build and deploy your application. With Gradle and Android Studio, it's pretty difficult for somebody without knowledge of developing for Android to create and upload a version. With fastlane, however, the knowledge needed can be as short as a single line. Running a 'fastlane beta' command on a computer that has fastlane installed (a process that in itself requires just one command) will run the 'beta' lane to perform all tasks from incrementing version values and signing the application through to uploading the app. This means deployment can be done by any team member or CI server (providing they are not using Windows).

So we've established fastlane is at least worth using within our projects. As such, I set about deploying this open source package in my current Android project, and all started well. Fastlane offers a lot of tools that allow you to get set up quickly, with the ability to assemble any build variant of the project, upload to HockeyApp, provide a changelog from the git commits, and post a build succeed/failure message to slack (particularly useful if the build is happening on a CI server). However, there was one task that was sorely missing from the Android fastlane actions.

It appears iOS has a utility called 'agvtool' which allows updating the apps' build values via the terminal. This allowed two standard fastlane actions called "increment_build_number" and "increment_version_number" to be created - but they were only supported by iOS. As  far as I could see, this meant it was impossible to achieve my goal of using fastlane since the version code on Android is used to identify each new build of an app. Without the ability to change the code, services such a HockeyApp and Google Play would not be able to manage updates and builds could not be differentiated.

Naturally, I attempted the simplest solution first. Fastlane comes with the ability to call through to Gradle tasks ( this is how it performs actions such as starting the app build), and so from my lane, I called through to the tasks that increment my version values. The problem then was that I hadn't actually gone anywhere; I'd simply changed the place from which the call to perform these actions was made. They still took a long time and it was necessary to add them to each project as custom methods in Gradle.

Next, I looked into fastlane plugins, and discovered the "Increment_version_code" plugin. This works by iterating over the application directory, finding the app's build.gradle file, attempting to find the 'versionCode' line, increasing the value and saving the file. This was along the right lines. However, several things stood out; I was, for example, confused about why the script looped over the entire directory, was struck by the inflexibility of looking for the first instance of 'versionCode' and with updating it, and noted a distinct lack of support for incrementing the version name.

By this time, I had decided that to get the functionality I desired, I would need to write my own fastlane actions. For this, I had three main goals:

  • The scripts should be provided with the file to update

  • The solution must be portable so it could be used in multiple projects without having to repeatedly copy and paste code

  • Most of the work should be performed by Fastlane, rather than calling a Gradle task. It must also support version code and version name alterations (in major.minor.patch format) for multiple build types in a single application.

What resulted from this were two separate fastlane actions -

"increment _android_version_code" and "increment_android_version_name" - both of which took the path to the app's build.gradle file as a parameter. The latter also took a version type. These actions worked in a similar way, but do require a small amount of Gradle set-up. Each project that uses these actions must define four variables in their Gradle files and set up any versionCode or versionName definitions to use these variables.

This small set-up, however, meant there would be a known place for fastlane to increment the versioning values. Likewise, if an application wished to set different values for different build types (debug builds, for example, have the version name major.minor.patch, whereas release builds only display major.minor) this would be possible simply by reusing the variables in each.

Each action is then fairly straightforward, taking the path to the Gradle file it looked for the variable to update, changing the value and saving the file. It then stores the new version code and version name in fastlane's shared variables, which can be accessed by the lanes and used for things like setting git tags etc.

The easiest way to demonstrate this is to see it in action. Below is a simple Fastfile lane that will increment the version code of the app. Then, if the script isn't running on a CI server, it will update the version name patch value. It will then build the release version of the application and upload the resulting APK to HockeyApp before committing the version increment to Git, adding a tag and (if running on a CI server) sending a slack notification about the successful deployment.


actions_path 'actions/' fastlane_version "2.0.2" default_platform :android platform :android do # Before running any lane, ensure there are no changes to commit, clean the repo, and update the build number before_all do |lane, options| # Ensure there are no uncommitted changes ensure_git_status_clean # Clean the project before we do anything gradle(task: "clean") # Call the method to increment the applications build number increment_android_version_code(path: ENV['GRADLE_PATH']) end lane :patch do if (!is_ci) #Update the version name and increment the patch value increment_android_version_name( path: ENV['GRADLE_PATH'], type: "patch" ) end #Generate the changelog based on commit messages since the last tag changelog = changelog_from_git_commits #Build the HockeyApp release through gradle gradle( task: "assemble" build_type: "release" flavor: "hockeyapp" ) #Upload the build to HockeyApp hockey( api_token: ENV["HOCKEYAPP_API_TOKEN"], notes: changelog, notes_type: "0", teams: ENV["TEAM_IDS"] ) #Commit the version bump to git, and tag the commit with the release version and push git_commit( path: File.dirname(ENV["GRADLE_PATH"]) message: "HockeyApp deployment version #{{lane_context[SharedValues::ANDROID_VERSION_NAME]}}" ) add_git_tag( tag: "v#{lane_context[SharedValues::ANDROID_VERSION_NAME]}" ) push_to_git_remote #Post a message on slack if the app was released successfully and running on a ci server if (is_ci) slack( message: "App release version code #{lane_context[SharedValues::ANDROID_VERSION_CODE]} pushed to HockeyApp", channel: "#app_channel" ) end end end


In conclusion, although fastlane currently doesn't support the same tools for Android as it does for iOS, it has the potential and flexibility to be used for projects on both platforms, simplifying the building and deployment of apps, and ultimately saving developer time. With Google acquiring fastlane, we're yet to see what is in store, but with any luck we'll soon have more support as the community grows. The range of tools available may even be expanded as Google integrates the Firebase, Fabric and fastlane teams. The solution above proved suitable to my needs - and will work for projects going forward. The actions can be found on this gist. Please feel free to recommend improvements or make a pull request.