Intermediate Fastlane

Introduction

So by now, if you’ve been working on mobile development for any longer than a few weeks, you’ve probably used Fastlane. I know I talk about it a lot in this blog, but it’s genuinely one of my favorite tools out there, and I think most people don’t use it to its full potential. So in this post, I’m going to show you some more advanced Fastlane features that you might not have seen before.

Separate Fastfiles

When you write your normal code, you won’t put everything in the same massive file, right? (RIGHT?) So why would you do that with your Fastlane setup? You can split your Fastlane setup into multiple files, which can make it easier to manage and understand.

I like to have all of my Fastlane files have fastfile in the name, so I can easily see which files are Fastlane files. So for example, I’ll have the main Fastfile, and a separate VersionBumpingFastfile for all of my version bumping logic (Which you can learn more about here!).

The way you do this is by using the import method in your Fastfile. So if you have a file called VersionBumpingFastfile in the same directory as your Fastfile, you can import it like this:

#!/usr/bin/ruby
# frozen_string_literal: true

fastlane_version '2.225.0'
import 'VersionBumpingFastfile'
# The rest of your normal Fastfile here..

By doing it like this, you can now call any lane in your VersionBumpingFastfile from your main Fastfile, or as standalone commands from the terminal, and you get the nice separation of concerns that you’re used to in your normal code.

before_all and after_all

Think about your normal XCTest suite (I know I know, you’re using the shiny new test framework, but try to remember what the olden days looked like). You have setUp and tearDown methods that run before and after each test, respectively. Fastlane has similar hooks that you can use to run code before and after your entire Fastlane run.

This is particularly useful if you’ve got things that need to happen regardless of which lane you’re running. For example, you might want to set up some environment variables, or clean up some files before you start running your lanes. You can do this with the before_all and after_all hooks in your Fastfile.

before_all do
  setup_circle_ci
end

lane :beta do
  build_app(scheme: "MyApp")
  upload_to_testflight
end

after_all do |lane|
  clean_build_artifacts
end

This sets up the environment for CircleCI and cleans up files after the Fastlane run. There are also some great examples of this on the Fastlane docs here, where they use the after_all block to notify a Slack channel when the Fastlane run is complete, which is mega useful.

Embrace the lane syntax

I know we’re all coming from different languages and different syntax, but Fastlane has its own way of doing things. Let’s look at some lane patterns that can make your code more readable and maintainable.

Firstly, the is_ci helper is your friend. It helps you write conditional lane logic. For example, instead of writing:

lane :deploy do |options|
  if ENV['CI'] == 'true'
    match(type: "appstore")
  end
  build_ios_app
end

You can write:

lane :deploy do |options|
  match(type: "appstore") if is_ci
  build_ios_app
end

You can also use lanes to handle error conditions elegantly:

lane :push_to_git do |options|
  begin
    push_to_git_remote
  rescue => ex
    handle_git_error(error: ex)
  end
end

private_lane :handle_git_error do |options|
  UI.error(options[:error])
  create_git_branch
  push_to_git_remote(force: true)
end

Another way you can make your lanes more readable is by using early returns for validation. For example, instead of writing:

lane :release do |options|
  if options[:version]
    if options[:version].match(/\d+\.\d+\.\d+/)
      increment_version_number(
        version_number: options[:version]
      )
      build_app
      upload_to_app_store
    else
      UI.user_error!("Invalid version format")
    end
  else
    UI.user_error!("Version is required")
  end
end

You can write:

lane :release do |options|
  UI.user_error!("Version is required") unless options[:version]
  UI.user_error!("Invalid version format") unless options[:version].match(/\d+\.\d+\.\d+/)

  increment_version_number(
    version_number: options[:version]
  )
  build_app
  upload_to_app_store
end

Conclusion

These are by no means the end all be all of Fastlane tips, but they’re some of the more advanced features that I think a lot of people don’t know about. There are a lot more great features, some of them are documented here, and some of them are just waiting for you to discover them. I hope this post has been helpful, and that you can take your Fastlane skills to the next level!