/dev/trouble
Eric Roller's Development Blog

Avoiding Scroll Bars in Fastlane

- Posted in iOS by

For a long time, I have been trying to avoid scroll bars in the screenshots that I generate. The solution that I came up with was simply to wait for them to disappear:

thirdBlock.element.swipeUp()

// Wait for the scroll indicators to be hidden
Thread.sleep(forTimeInterval: 2.5)

takeSnapshot("Last_Block")

I have also been using fastlane for several years to automate this. While I try very hard never to add any screenshot-specific exceptions to the code, there has always been the odd need to do it, for instance to handle situations that are not supported by the iOS simulator.

Preprocessor Macros

For code sections that are only used during development, you should mask them out using "preprocessor macros" like:

#if DEBUG
print("Data finished loading.")
#endif

This works using an Xcode project setting that defines SWIFT_ACTIVE_COMPILATION_CONDITIONS to use DEBUG, which results in the command-line option -D DEBUG to be used by CompileSwift. You can see this if you dive deep into your build logs.

Similarly, you can mask sections that are only (or never) to be used in the simulator like this:

#if targetEnvironment(simulator)
    // iOS simulator only
#else
    // iOS device only
#endif

Simple inversions are also supported in Swift using an exclamation mark (read it as: "not"):

#if ! targetEnvironment(simulator)
    // iOS device only
#endif

Fastlane-Specific Code

The same can be done for fastlane-specific code, but it is necessary to define a macro, for instance in the fastlane/Snapfile. Here we define FASTLANE:

# Add a define for xcodebuild
xcargs "SWIFT_ACTIVE_COMPILATION_CONDITIONS=FASTLANE"

which allow us to use in the code:

#if FASTLANE
    // fastlane only
#endif

No Scroll Bars in Fastlane

Using the above macro, I have now simply disabled the scroll bar indicators in the code:

override func viewDidLoad() {
    super.viewDidLoad()

    navigationItem.rightBarButtonItem = editButtonItem

    #if FASTLANE
    tableView.showsVerticalScrollIndicator = false
    tableView.bounces = false
    #endif
}

And this means that the test script can be made to run without additional delays (compare above):

thirdBlock.element.swipeUp()
takeSnapshot("Last_Block")

[UPDATE 2021-01-21] In SwiftUI, you could use:

List {
    ...
}
.onAppear {
    #if FASTLANE
    UITableView.appearance()
        .showsVerticalScrollIndicator = false
    #endif
}