You don’t need GUI — how to control iOS Simulator from command line
Back in past, if you used a computer, you mostly used a command line user interface. Today for development you use GUI (Xcode, Simulator, etc.) and it’s pretty easy to use. But what to do if you need to use CI or you want to automate some processes, like create/delete specific simulator, change some settings? In this case you can use Xcode’s simctl utility and bash scripts. Below you will find a manual how to write a bash script which will simplify the work with iOS simulator using CLI.
The script should be able to manage the following actions:
getuid
create
delete
boot
open
shutdown
And another action that will be implemented is changing the language settings of the simulator (can be any other setting) using configuration file.
For all these actions the mandatory name
parameter will be used.
Bash script, initial setup
Create bash file and make it executable:
$ touch ios_simulator_cli.sh
$ chmod +x ios_simulator_cli.sh
Open it in the editor and add the following content:
#!/bin/bash
SIMULATOR_NAME="$1"
COMMAND="$2"print_params () {
echo "print_params"
}get_uid() {
echo "get_uid"
}create () {
echo "create"
}delete () {
echo "delete"
}boot () {
echo "boot"
}open () {
echo "open"
}shutdown () {
echo "shutdown"
}locale () {
echo "locale"
}if [ -z $SIMULATOR_NAME ] || [ -z $COMMAND ]
then
print_params "Simulator name and COMMAND has to be specified"
exit 1
ficase $COMMAND in
("getuid") get_uid ;;
("create") create $3 $4 ;;
("delete") delete ;;
("boot") boot ;;
("open") open_simulator ;;
("shutdown") shutdown ;;
("locale") set_locale $3 ;;
(*)
print_params "Unknown COMMAND [$COMMAND]"
exit 1
;;
esac
Now let’s implement all these functions:
print_params
This is a helper that can be used to define which parameters the script supports and how to use it. The content will be:
print_params () {
echo "$1. Example:
./scripts/ios_simulator_cli.sh SIMULATOR_NAME COMMAND
Where COMMAND can be:
create - create new simulator
shutdown - shutdown simulator
delete - delete simulator
boot - boot simulator
open - open simulator
locale - set locale
getuid - get simulator uid"
}
get_uid
All actions with simctl
will require simulator uid. To get it the following actions actions are required:
- Get a list of all devices with
xcrun simctl list
- Filter result by name using
grep
- And a bit of magic of
awk
to parse the result and getuid
As a result you will get a function:
get_uid() {
echo $( xcrun simctl list | grep -w $SIMULATOR_NAME | awk 'match($0, /\(([-0-9A-F]+)\)/) { print substr( $0, RSTART + 1, RLENGTH - 2 )}' )
}
Execute the script:
$ ./ios_simulator_cli.sh my_simulator getuid
and if simulator exists, the script will print uid, otherwise empty result.
create
To create new simulator simctl
requires the following parameters:
name
- can be any name, for example "my_simulator"device type
- usexcrun simctl list devicetypes
command to get the complete list of devicesruntime
- usexcrun simctl list runtimes
command to get the complete list
And the content for create
function will be:
create () {
TYPE=$1
RUNTIME=$2
SIMULATOR_ID=$( xcrun simctl create $SIMULATOR_NAME com.apple.CoreSimulator.SimDeviceType.$TYPE com.apple.CoreSimulator.SimRuntime.$RUNTIME )
echo $SIMULATOR_ID
}
Now you can run the script to create new simulator:
./ios_simulator_cli.sh my_simulator create iPhone-7-Plus iOS-11-4
delete/boot
To delete or boot the simulator simctl
requires only the simulator uid and corresponding command. Therefore the content for these function is:
delete () {
SIMULATOR_ID=$( get_uid )
xcrun simctl delete $SIMULATOR_ID
}boot () {
SIMULATOR_ID=$( get_uid )
xcrun simctl boot $SIMULATOR_ID
}
open
To open simulator, the open
command can be used:
open_simulator () {
SIMULATOR_ID=$( get_uid )
open -a Simulator --args -CurrentDeviceUDID $SIMULATOR_ID
}
shutdown
And the last step is to shutdown the simulator. The function will need to check the current status and the implementation is similar to get_uid
function:
get_status () {
echo $(xcrun simctl list | grep -w $SIMULATOR_NAME | awk 'match($0, /\(([a-zA-Z]+)\)/) { print substr( $0, RSTART + 1, RLENGTH - 2 )}')
}
and will be implemented using a loop to check the status:
shutdown () {
SIMULATOR_ID=$( get_uid )
RETRY_DELAY=5
retry_cnt=10 for (( ; ; ))
do
SIMULATOR_STATUS=$(get_status) if [ $SIMULATOR_STATUS = "Shutdown" ]
then
break
elif [ $SIMULATOR_STATUS = "Booted" ]
then
xcrun simctl shutdown $SIMULATOR_ID
else
echo "Retrying in $RETRY_DELAY seconds, status is [$SIMULATOR_STATUS]"
fi if [ $retry_cnt = 0 ]
then
echo "Cannot shutdown simulator $SIMULATOR_ID"
exit -1
fi
retry_cnt=$((retry_cnt - 1))
sleep $RETRY_DELAY
done
}
That’s all! Basic functionality which uses xcrun simctl
is implemented and it maybe enough for your needs, otherwise you can keep extending the script.
Extended settings
If you want to change some settings that you usually do in the simulator, for example, language/locale, you can modify the configuration file .GlobalPreferences.plist
which is located in:
"$HOME/Library/Developer/CoreSimulator/Devices/$SIMULATOR_ID/data/Library/Preferences/
It's a binary file and there are some different ways in which you can modify it. The easiest way is to use Xcode or some editor like TextWrangler, but since we do everything only with CLI we will use another way. There is a tool called plutil
which allows to convert plist files to XML or binary in Mac OS, replace properties in the settings, etc.
Navigate to $HOME/Library/Developer/CoreSimulator/Devices/$SIMULATOR_ID/data/Library/Preferences/
folder (in my case I have 04BABE1A-7398-47AE-99F2-165AC642F578 uid) and convert .GlobalPreferences.plist
to XML (before you can also make a copy of this file):
$ cd $HOME/Library/Developer/CoreSimulator/Devices/04BABE1A-7398-47AE-99F2-165AC642F578/data/Library/Preferences/
$ cp .GlobalPreferences.plist .GlobalPreferences.plist.orig
$ plutil -convert xml1 .GlobalPreferences.plist
Now check the content of the file:
$ cat .GlobalPreferences.plist
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>AppleLanguages</key>
<array>
<string>en</string>
</array>
<key>AppleLocale</key>
<string>en_US</string>
</dict>
</plist>
There are some properties defined. Do not forget to convert the file back to binary:
$ plutil -convert binary1 .GlobalPreferences.plist
Now open the simulator, using the just created script:
$ ./ios_simulator_cli.sh my_simulator boot
$ ./ios_simulator_cli.sh my_simulator open
In the simulator navigate to Settings -> General and change some settings. For example, Language & Region to Ukrainian
Close simulator and convert .GlobalPreferences.plist
to xml again to check the difference. There will be some changes including:
AppleKeyboards
key, which has value<string>uk_UA@sw=Ukrainian;hw=Automatic</string>
AppleLanguages
key, with<string>uk-US</string>
value- etc.
This is exactly what is needed and what can be changed using command line and plutil
. Revert the changes to the settings file and define the locale
function in bash script:
set_locale () {
LOCALE=$1
SIMULATOR_ID=$( get_uid )
PLIST_FILE="$HOME/Library/Developer/CoreSimulator/Devices/$SIMULATOR_ID/data/Library/Preferences/.GlobalPreferences.plist"
plutil -replace AppleLocale -string $LOCALE $PLIST_FILE
plutil -replace AppleLanguages -json "[ \"$LOCALE\" ]" $PLIST_FILE
plutil -replace AppleKeyboards -json '[ "uk_UA@sw=Ukrainian;hw=Automatic" ]' $PLIST_FILE
}
the function checks for the simulator and using plutil
replaces the AppleLocale
, AppleLanguages
and AppleKeyboards
keys in settings file. Now you can run the script to change the language and boot the simulator:
$ ./ios_simulator_cli.sh my_simulator locale uk_UA
$ ./ios_simulator_cli.sh my_simulator boot
$ ./ios_simulator_cli.sh my_simulator open
and you will get the simulator with the language you just defined:
In the same way you can try to change other settings and write bash script functions for this.
Conclusions
There is nothing complicated to use CLI, bash
scripts or utilities like simctl
, plutil
, etc. And such bash scripts can simplify your daily work, especially if you would like to automate some processes or use CI (which can be another bash script):
./ios_simulator_cli.sh my_simulator create iPhone-X iOS-11-4
./ios_simulator_cli.sh my_simulator locale uk_UA
....
# do some tests
...
./ios_simulator_cli.sh my_simulator delete
You can also download entire script and try it. The script was written and tested with Xcode 9.4.