Swift Package that bridges Apple's private CoreSimulator / SimulatorKit
frameworks and exposes high-level APIs for driving a booted iOS Simulator from a
macOS host app.
import SimKit
import SwiftUI
struct MySimulator: View {
var body: some View {
SimulatorScreen(deviceUDID: "<UDID-of-a-booted-simulator>")
.aspectRatio(9.0 / 19.5, contentMode: .fit)
}
}Frames come straight from SimulatorKit's IOSurface framebuffer pipeline — zero
pixel copies, runs at the device's native refresh rate (60/120 Hz). No simctl io screenshot polling.
SimulatorScreenView—MTKViewthat renders the live framebuffer and forwards mouse / keyboard / scroll events back as HID input.SimulatorScreen—NSViewRepresentablewrapper for SwiftUI.SimulatorInteractionState—@Observablechannel for cursor / touch state, for overlay UIs.SimulatorBiometrics— Face ID / Touch ID match / non-match / enrolled toggle.SimulatorControl— erase, restart, openURL, appearance (light/dark), pasteboard, memory warning, iCloud sync, Darwin notifications.SimulatorLocation— clear / set / playsimctlroute presets.SimulatorOrientation— rotate the simulator via mach-message dispatch.SimulatorStatusBar— App-Store-screenshot status-bar overrides.
Used by SimDeck.
- macOS 14+
- Xcode 26+ (Swift 6.3 toolchain)
- An Xcode install in
/Applications/(SimKit locatesSimulatorKitat runtime viadlopen—xcode-select -pfirst, then a/Applications/scan). - The target simulator must be booted before
attach(usexcrun simctl boot).
./scripts/install-hooks.shInstalls the pre-commit hook that runs swift format over staged Swift files
using .swift-format.
Sources/
Display/ — SimulatorScreen, SimulatorScreenView, MetalRenderer,
SimulatorFramebuffer, SimulatorInteractionState
Input/ — Device buttons / edges / keys / sizes, plus the HID
dispatchers (HIDInput, IOHIDDigitizerDispatch)
Control/ — simctl/mach wrappers (Biometrics, Control, Location,
Orientation, StatusBar)
Runtime/ — Framework bridging (CoreSimulatorRuntime, SimDeviceResolver,
RuntimeInvoke / dlerrorString)
SimKitError.swift
The package builds in Swift 6 language mode with
NonisolatedNonsendingByDefault upcoming feature enabled, so @concurrent is
available on async methods. UI types (SimulatorScreenView, MetalRenderer,
SimulatorInteractionState, ScreenViewBox, ScreenRecorder) are
@MainActor; everything else (Process / mach / dlopen wrappers) is plain
Sendable.
The package never imports SimulatorKit or CoreSimulator. Instead it
dlopens both at runtime and reaches into the classes via the Objective-C
runtime:
SimServiceContext.sharedServiceContextForDeveloperDir:error:defaultDeviceSet.availableDevices→ findSimDeviceby UDIDdevice.io.deviceIOPorts→ ports whoseportIdentifieriscom.apple.framebuffer.displayport.descriptor.registerScreenCallbacksWithUUID:...→ callbacks fire on every frameport.descriptor.framebufferSurfacereturns anIOSurfacewe hand to the Metal renderer.
Linking the frameworks at build time would bake LC_LOAD_DYLIB entries that
dyld must resolve before main(), which breaks when Xcode lives outside
/Applications/Xcode.app. Runtime dlopen handles every install location.
Apache 2.0. See LICENSE and NOTICE.
This package adapts the IOSurface streaming logic from tddworks/baguette (Apache 2.0). NOTICE lists the modifications.