You don’t need GUI — how to control iOS Simulator from command line

Maksym Rusynyk
ITNEXT
Published in
6 min readAug 11, 2018

--

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
fi
case $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 get uid

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 - use xcrun simctl list devicetypes command to get the complete list of devices
  • runtime - use xcrun 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.

--

--