How to override simulator status bar for App Store screenshots

Recently I have added localization for 9 regions to my app KINN – Contacts & Groups,. It immediately made me realise that taking screenshots for all supported regions wasn’t going to make for an efficient manual task. After automating taking screenshots for all locales using test plans – a new feature in Xcode 11 – I noticed the screenshots were reflecting the low laptop battery and had different times. Those inconsistencies didn’t make for great screenshots. So today I want to show you how to override simulator status bar before running your UI tests to get consistent time, battery and carrier across all regions and screenshots.

It’s worth noting this article is based on all the sources found at the end of the article and it only applies for Simulator running iOS 13 and above. The simctl function we are going to use has a new argument status_bar since Xcode 11, so it won’t work for earlier versions of iOS/Xcode.

Override simulator status bar in a pre-action

We will start by editing the scheme we used for running the UI Screenshot Tests, by adding a script as a pre-action when running the tests.

XCode Project Edit Scheme

Under the Test section in the sidebar, let’s go ahead and select Pre-actions and create a New Run Script Action named “Override simulator info”. The name is not that important, but it’s better to name it clearly now so you can easily recognise what it does in the future.

The script below is not mine, but copied from Jeff Kelley’s article. It includes a couple of optimizations for fastlane and you can read more about it the linked article.

function version {
    echo "$@" | awk -F. '{ printf("%d%03d%03d%03d\n", $1,$2,$3,$4); }';
}

# Don’t run on iOS devices.
if [[ "${SDKROOT}" != *"simulator"* ]]; then
    exit 0
fi

# Don’t run on iOS versions before 13.
if [ $(version "${TARGET_DEVICE_OS_VERSION}") -ge $(version "13") ]; then
    xcrun simctl boot "${TARGET_DEVICE_IDENTIFIER}"

    xcrun simctl status_bar "${TARGET_DEVICE_IDENTIFIER}" override \
        --time "9:41 PM" \
        --dataNetwork wifi \
        --wifiMode active \
        --wifiBars 3 \
        --cellularMode notSupported \
        --batteryState discharging \
        --batteryLevel 100
fi
Ensure the UI Tests target is selected

Once you paste the script, make sure you select the UI tests target where you take screenshots under “Provide build settings from”. That is where the TARGET_DEVICE_IDENTIFIER and other environment variables are inherited from, so we need to make sure they are available when the script is excuted.

Understanding the pre-action bash script

Let’s quickly go over what the script does, so it’s clear what is happening.

First, we are defining a shell function called version to transform the device OS version like “13.2.1” into a number that will look like 13002001000. This function will help us compare between different OS versions. This is a trickier task because unlike numbers with decimals, 13.1 is smaller than 13.10 for instance. That’s why those leading zeros are added, so when converting to a number we end up with a bigger number for 13.22.

Next, we will exit the script early if the device that the script is executed on does not contain “simulator” in the name. This is because TARGET_DEVICE_IDENTIFIER which we’ll use in the following commands won’t make sense when passed to the simctl command if the device is not a simulator.

Finally, in the last if statement we check if the current device version is greater or equal (notice the -ge) than version “13”. If that evaluates to true, we boot the simulator with the provided identifier, and use the status_bar command on xcrun simctl to override the status bar properties we’re interested in.

Reset the overridden simulator settings

When tests finish running, it’s a good idea to reset the overridden simulator settings so we leave the simulator in the state we found it when we started. Let’s also add a post tests action.

function version {
    echo "$@" | awk -F. '{ printf("%d%03d%03d%03d\n", $1,$2,$3,$4); }';
}

# Don’t run on iOS devices.
if [[ "${SDKROOT}" != *"simulator"* ]]; then
    exit 0
fi

# Don’t run on iOS versions before 13.
if [ $(version "${TARGET_DEVICE_OS_VERSION}") -ge $(version "13") ]; then
    xcrun simctl boot "${TARGET_DEVICE_IDENTIFIER}"
    xcrun simctl status_bar "${TARGET_DEVICE_IDENTIFIER}" clear
fi

Again, let’s go through what this script does. We already talked about the version function and the early exit, to ensure the script only runs when the device is a simulator. Next, if the version of the target simulator is greater or equal to 13, then we clear out the overridden settings for the booted simulator using the clear argument.

If you want to find out more about what you can override feel free to run the following command in your terminal:

$ xcrun simctl status_bar help

And with that, when running the UI tests for the specified target, the simulator uses the values we have overridden. If you are interested in how to achieve the same results for iOS versions earlier than iOS 13, SimulatorStatusMagic.

Other resources

This article was last updated on 31 May 2020.