Control the simulator... in Swift
SimulatorServices provides an easy to use API for managing, querying, and accessing simulators on your Mac.
Apple Platforms
- Xcode 13.3 or later
- Swift 5.5.2 or later
- iOS 14 / watchOS 6 / tvOS 14 / macOS 12 or later deployment targets
Linux
- Ubuntu 18.04 or later
- Swift 5.5.2 or later
Use the Swift Package Manager to install this library via the repository url:
https://github.com/brightdigit/SimulatorServices.git
Use version up to 1.0.1
.
SimulatorServices allows you to execute subcommands to simctl
directly in Swift while offering an easy to use API for parsing and passing arguments.
SimulatorServices uses the SimCtl
object to pass subcommands. Each subcommand objects takes custom arguments or property and can parse the standard output into an easy to use Swift object. There are currently two supported subcommands: GetAppContainers
and List
.
The List
subcommand gives you the ability pull the list of devices, device types, runtimes, and device pairs. For instances let's say you want to pull all your available devices which are booted:
let simctl = SimCtl()
let list = try await simctl.run(List())
let devices = list.devices.values
.flatMap { $0 }
.filter{$0.state == "Booted"}
For more details on the properties available, check out the documentation on SimulatorList
.
In this instance, we can take this a step further and find app container directories for these different simulator devices.
With our list of device simulators, we can use the GetAppContainer
subcommand to find specific paths. In this case, let's find the data directory of our app com.BrightDigit.Jojo.watchkitapp
:
let jojoSimulatorDataDirPaths: [Path] = await withTaskGroup(of: Path?.self) { taskGroup in
for device in devices {
taskGroup.addTask {
do {
return try await simctl.run(
GetAppContainer(
appBundleIdentifier: "com.BrightDigit.Jojo.watchkitapp",
container: .data,
// use the udid of the device to indicate which simulator to pull from
simulator: .id(device.udid)
)
)
// if the data is missing that means that device does not contain that app container
} catch GetAppContainer.Error.missingData {
return nil
} catch {
return nil
}
}
}
return await taskGroup.reduce(into: [Path]()) { paths, path in
// essential this does a compactMap on results
if let path {
paths.append(path)
}
}
}
For more details on arguments available, check out the documentation on List
.
While now this package only supports two subcommands, however there are two ways more subcommands can be supported:
- add an issue and it will be implemented in the future by the organizers
- add an issue and a pull request which implements that
Subcommand
For details on how to implement a new Subcommand
, check out the code on the existing commands and take a look at the documentation of the Subcommand
protocol.
There are some great articles out there regarding the intracacies of simctl
. I highly recommend these articles which helped me in building this package:
Also take a look at these great app which take advantage of what simctl
can do:
This code is distributed under the MIT license. See the LICENSE file for more info.