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.
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
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
- https://www.jessesquires.com/blog/2019/09/26/overriding-status-bar-settings-ios-simulator/
- https://www.hackingwithswift.com/example-code/xcode/how-to-set-the-clock-in-the-ios-simulator
- https://www.detroitlabs.com/blog/2019/09/19/fixing-simulator-status-bars-for-app-store-screenshots-with-xcode-11-and-ios-13/
- https://apple.stackexchange.com/questions/83939/compare-multi-digit-version-numbers-in-bash/123408#123408
This article was last updated on 31 May 2020.