FBSimulatorControl
FBSimulatorControl
is the macOS Framework that implements all functionality associated with iOS Simulators within idb
. It can be used independently of idb
as it is a standalone Framework.
CoreSimulator.framework
β
CoreSimulator
is the Private Framework that is the interface for most Simulator related functionality on macOS. In previous Xcode versions, CoreSimulator
was bundled inside of Xcode, but it is now installed at the System level just like MobileDevice.framework
. It may be upgraded to a newer version as part of the install process of Xcode itself.
CoreSimulator
is used by Xcode and simctl
as the Framework used to manipulate Simulators. It has Objective-C classes representing a set of Simulators within a directory (SimDeviceSet
wrapped by FBSimulatorSet
) and an individual iOS Simulator (SimDevice
, wrapped by FBSimulator
). There is also a Class that behaves a lot like an "entrypoint" to the Framework in SimServiceContext
, this performs initialization of external services and is aware of the various configurations of Simulators that are availabile.
simctl
β
simctl
is essentially a CLI that exposes iOS Simulator functionality by linking and using CoreSimulator
. This binary is bundled inside of Xcode, and typically addressed via usage of the xcrun
command. xcrun
is essentially a trampoline that addresses binaries that are bundled within Xcode by using the value defined in xcode-select
The overwhelming majority of Simulator functionality is not implemented in simctl
, it is implemented within CoreSimulator
with simctl
providing an accessible way of using this functionality. Having this behaviour implemented at the Framework level so that Simulator.app
and simctl
behave identically when using their user interfaces.
CoreSimulatorService
β
CoreSimulatorService
is a user-level daemon that is bootstrapped by any usage of SimServiceContext
, effectively any usage of iOS Simulators will cause this service to be created and launched. This is an XPC service contained within the CoreSimulator.framework
bundle. This service is responsible for starting and managing Simulators.
When using CoreSimulator
as a client Framework it will transparently communicate with CoreSimulatorService
. The overwhelming majority of CoreSimulator
APIs that do meaningful work are essentially performing IO to CoreSimulatorService
, though the asynchronous nature of this work isn't completely consistent. Some CoreSimulator
APIs (for instance those associated with launching an iOS Application) do have asynchronous methods, but others (such as the instantiation of a SimServiceContext
) do not. CoreSimulatorService
still gets much of it's implementation from CoreSimulator.framework
, touching different areas of the API.
Having the "work" of iOS Simulators performed within the context of share user daemon is likely due to the needs to synchronize and consolidate state. The service is also an effective caching mechanism for runtime and device profiles. There are a few downsides to this approach. Firstly, CoreSimulatorService
is effectively a single point of failure. If CoreSimulatorService
becomes stuck, or a client of CoreSimulator
exhibits pathological behaviour, then all iOS Simulator functionality on a given host will fail. iOS Simulator functionality will effectively halt until CoreSimulatorService
restarts, either by the hung CoreSimulatorService
terminating and restarting or via reboot.
Secondly, the lifecycle of CoreSimulatorService
is tied to that of the selected Xcode
. This means that different versions of Xcode cannot be used concurrently on the same host; CoreSimulatorService
can only be aware of a single Xcode at any point in time. Switching Xcodes and fetching a new CoreSimulatorService
(for instance via a simctl
command) will cause CoreSimulatorService
to restart, disconnecting existing clients and killing booted Simulators.
SimRuntime
β
An iOS Simulator Runtime is all of the required components for running an iOS Simulator of a given iOS version. This is a bundle, where the contents closely match the makeup of the files on disk on a physical device. This includes binaries that are compiled for the host architecture (x86_64 in the case of Intel Macs, ARM64 in the case of ARM based Macs) as well as Frameworks. The Frameworks within a SimRuntime match those of iOS, instead of those of the macOS host. There are often subtle differences in the iOS and macOS APIs, even within the same Framework. A single SimRuntime
represents a single iOS version.
Each version of Xcode is bundled with SimRuntime
s for the most recent iOS version that is relevant for the Xcode version across iOS, tvOS and watchOS. However, additional iOS Versions can be supported on a given version of Xcode via the "Components" section within Xcode.app
. These bundles are then installed into /Library/Developer/CoreSimulator/Profiles/Runtimes
on the host system. Runtime bundles are backwards, but not forwards compatible. For example, Xcode 11 has support for iOS 13 (the latest iOS version associated with this Xcode version) and earlier versions of iOS, but not for iOS 14.
launchd_sim
β
iOS, like macOS has launchd
as it's "root process" (often PID 1). However, iOS Simulators have their own version of launchd
as a root process. This launchd_sim
is effectively the "root process" of the iOS Simulator runtime, but not of the macOS host. This launchd_sim
is required by the Simulator OS in order to launch Applications, manage services etc. Each launched iOS Simulator has it's own launchd_sim
process, launched from the launchd_sim
within the SimRuntime
. This also means that processes within this nested launchd
will only see the processes of the iOS Simulator, rather than all of those of the entire host (including other iOS Simulators running on the same host).
This launchd_sim
can be interrogated the same as the launchd
of the host, provided that the launchctl
called is spawned within the launchd_sim
of the iOS Simulator.
Device Setsβ
A Device Set is essentially a directory that contains a number of created iOS Simulators. The "Default Device Set" is located at ~/Library/Developer/CoreSimulator/Devices
, this is the device set that is used by Xcode.app
.
Custom device sets can be placed at any location on disk. This is useful for isolating the filesystems of created iOS Simulators from each other. For instance, if there are independent processes managing iOS Simulators on the same host it can be worthwhile having each of these processes manage their own device sets to prevent data races.
There is also an XCTestDevices
directory at ~/Library/Developer/XCTestDevices
. This is the set of Simulators that are used by xcodebuild
, distinct from the user interface. This means that xcodebuild
can manage and use it's own set of iOS Simulators, independent of the Xcode UI. This may exist for a similar reasons to why custom device sets are practical for automation scenarios. It would also be a confusing user experience if an iOS Simulator that was being used within xcodebuild
was using an iOS Simulator that a user was using via Xcode when running UI Tests.
Simulator.app
β
This is the "Simulator" Application with which most developers will be familiar with. This Application effectively mirrors the state of launched iOS Simulators within CoreSimulatorService
. It is not an essential part of booting and managing iOS Simulators; iOS Simulators can be booted and used without a Simulator.app
launched for it. This makes using Simulators more practical in automation scenarios where a running macOS Application representing the iOS Simulator is not important or even desirable.
The Simulator Application will default to showing all iOS Simulators that are within the "Default Device Set". This means that booted iOS Simulators within "Custom Device Sets" will not be displayed within Simulator.app
.
The functionality within this Application is largely implemented within CoreSimulator.framework
and SimulatorKit.framework
, with the UI implemented directly within the application itself.
Framebuffers via IOSurface
β
The screen from an iOS Simulator is rendered, regardless of whether there is an iOS Simulator application that is presenting this within a IO. An iOS Simulator can be launched independently of Simulator.app
, since Simulators are kept alive by CoreSimulatorService
.
In order for other Applications (mainly Simulator.app
, but also for video recording within simctl
) to get the iOS Simulator's Framebuffer for rendering, CoreSimulator
can access the IOSurface
of the screen of an iOS Simulator. A Simulator can have many screens, for instance when Simulating CarPlay and the main screen at the same time.
An IOSurface
is an object that wraps a Framebuffer, with the contents of the Framebuffer being located within GPU memory. This IOSurface
can be read and inspected across process boundaries. The iOS Simulator uses this IOSurface
as the backing Framebuffer for it's view of an iOS Simulator.
IOSurface
objects are also easily convertable to "Pixel Buffer" types that are used in video encoding. This allows FBSimulatorControl
to implement video encoding of an iOS Simulator's Framebuffer in a way that avoids large copies of bitmap framebuffers on a per-frame basis.
IndigoHID
β
"Indigo" is a service present in the iOS Simulator that is used inside Simulator.app
to synthesize "Input Events" that are understood within the iOS Simulator. This service is how clicking on the UI of the Simulator.app
translate into touches within the iOS Simulator.
This uses "mach" IPC, where data structures are sent over a channel using mach_msg_send
. These data structures are defined through the "Mach Interface Generator", which get compiled out of the Simulator.app
binary. As such, FBSimulatorControl
's understanding of the layout and values in these data structures are understood through reverse engineering.
The reverse engineering of this protocol, allows FBSimulatorControl
to expose APIs that allow sending of touch events directly to the iOS Simulator without using Accessibility APIs in a UI Test. The combination of video streams and APIs for sending input events allows for the building of applications that expose a remote iOS Simulator.
SimulatorKit.framework
β
This is another macOS Framework that is used in iOS Simulator management. This Framework is not installed to the System, it is bundled within Xcode. Instead, this Framework is more used to implement functionality within Simulator.app
.
For example, this Framework contains some of the Indigo
client functionality for sending input events.