Top 5 Secrets to speed up Fastlane iOS

Speed up fastlane iOS
Speed up fastlane iOS

Fastlane is my goto tool to speed up the delivery process of my iOS apps. Though it can become blazing fast and don’t hold up your CI job, when we optimise it. I have learned these tips over a period of time by practicing it.

These are 5 most time consuming processes using fastlane:

  1. Compiling the App
  2. Unit Testing / UI Testing
  3. Functional Testing – like Cucumber
  4. Generating Framework/XCFramework
  5. Publishing the App

So how do we reduce time?

Compilation will happen and it will take time what it takes we can’t do anything, but at least don’t compile multiple times unnecessarily.

Testing will take time what it takes we can’t do anything, but at least we can use multiple cores of CPU that we already have.

We generally have multiple computers or node in the CI (Jenkins/Circle) can we put those to use instead of using one computer at a time?

Let’s see how I optimised my lanes to become super-fast lanes:

TestWithoutBuilding

Generally in the CI job we compile the code first and then it moves to next step where it executes the tests, it may be Unit Test, UI Test or Functional tests.

When testing it actually rebuilds again which means 2 times the amount of time it should take.

We should use testWithoutBuilding = true which reuses the existing derived data and doesn’t compile again.

scan(
     workspace: File.absolute_path('../bitMountn/bitMountn.xcworkspace'),
     scheme: 'bitMountn',
     testWithoutBuilding: true,
     derivedDataPath: "../bitMountn"
     test: true,
     clean: false
    )

Now few important points to note here:

  1. When compiling the app, make sure to set custom derivedDataPath. It will hep you to collect the derived data and reuse when it actually starts executing tests.
  2. If you are using any Scan file, make sure derived data path is set correctly derivedDataPath = "../bitMountn"

What to do when there are multiple node/computers job is using:

You may ask how we can do when we have multiple nodes or computers executing these steps, well there is a solution as well.

When there are multiple nodes involved it can actually copy the derived data from one node to another. It takes time to copy but it will take 10% of the compile time.

multi_scan

By the name multi_scan says that we can do multiple scan. Scan is a fastlane function to do a lot of stuffs like build test clean etc.

There are generally multiple cores available right? but are you really utilising all these cores at one point of time?

we should definitely utilise those resources parallely, I mean there are

multi_scan is a great fastlane plugin written by lyndsey-ferguson. It helps running test cases in different simulators parallelly. It also help i retrying a failed test case.

Test case can fail due to hundreds of reasons, there may be lack of computer memory which has nothing to do with your code. So retrying a test case is important.

multi_scan(
          workspace: File.absolute_path('../AtomicBoy/AtomicBoy.xcworkspace'),
            scheme: 'AtomicBoy',
            fail_build: false,
            try_count: 2,
            disable_xcpretty: true
          )

It also can run the test cases in batches in each simulator. Which means let’s say there total 50 test cases and it can create a batch of 10 test cases and run these 10 in a single simulator.

Checkout multi_scan in github

Parallel Testing

parallel testing in iOS Simulator

When you select execute in parallel on Simulator, it actually runs the test cases in parallel in different simulators based available cores.

By default test are executed by xcode in alphabetical order. But when select randomise execution order it tests the test cases in random order.

Not Cleaning

Do we really always need to clean the build before every operation like testing or publishing no, it’s not required.

We should avoid as much as possible cleaning up, because it cleans up derived data and compiles all over again.

scan(
     workspace: File.absolute_path('../bitMountn/bitMountn.xcworkspace'),
     scheme: 'bitMountn',
     build: true,
     clean: false
    )
scan(
     workspace: File.absolute_path('../bitMountn/bitMountn.xcworkspace'),
     scheme: 'bitMountn',
     test: true,
     clean: false
    )

In the above two lanes, It is compiling two times because it cleaned up derived data in the first lane.

There is one more place where we tend to set clean = true is in Scanfile. Scanfile can have these params and ultimately effect every lanes that usage scan

Be very careful about Scanfile, because it applies to all the lanes that usage scan sometime unintentionally we set some flags, and it has more side effects than effects.

What to do when clean is really required because of compilation error?

I will take a lazy approach to it, which means only when, there is a compiler error I will retry compilation by first cleaning it.

Such compiler error due to bad cache happens only once in a while, and you are saving a lot of useful CI time.

Retry Test

I have seen numerous times when one test case failed and because of that the final result is CI job failed and the release unsuccessful.

One or two test case failures might have nothing to do with actual code rather it failed due to lack of CI resource for example low memory, simulator failed to launch etc.

We are waisting the precious time of a CI job just because of one or two test case failures which has nothing to do with code or may be it’s a genuine test case failure.

Retry is so important in such scenarios. You’re going be doubly sure when a test case fails after retry means it’s a genuine failure and you need to fix it.

multi_scan have this amazing feature of retrying a test case.

1 Comment

Comments are closed