SwifterKit wraps DriverKit for Swift 6 driver authors. Driver configuration and behavior stay in Swift; SwifterKit owns the generated C++/IIG boundary required by the DriverKit ABI.
Use SwifterKit when a driver needs:
- a generated DriverKit extension project without application-owned C++, C, Objective-C, or IIG glue;
- typed commands and events for supported DriverKit families;
- raw user-client access when no capability-specific API fits; or
- one Swift concurrency model for driver lifecycle, events, and completion work.
- Swift 6.2 or later for package consumers
- the Swift version in
.swift-versionfor local development; CI alone runs the Swift 6.1 compatibility build - macOS 10.15 or later for the Swift package and base DriverKit runtime
- Xcode with the DriverKit SDK for generated extension builds
- Apple-approved entitlements, signing assets, a host application, and physical hardware for deployment testing
Signing is not required to build or test the package or its generated extension project locally.
DriverKit family frameworks were added across later releases. DriverExtensionGenerationOptions.deploymentTarget defaults to 19.0; the generator rejects a capability when its native runtime needs a newer DriverKit version.
The package target and the generated extension target are independent. Swift package clients can run on macOS 10.15 while a generated extension selects the narrowest DriverKit target required by its capabilities.
| Generated capability | Minimum DriverKit target | Host availability |
|---|---|---|
| Base runtime, HID, USB, serial, interrupts, memory | 19.0 | macOS 10.15 |
| PCI | 19.0 | macOS 11.1 |
| SCSI controller | 20.4 | macOS 11.3 |
| Block storage, audio | 21.0 | macOS 12 |
| Networking, SCSI peripheral | 22.0 | macOS 13 |
| MIDI | 24.0 | macOS 15 |
| Video | 27.0 | macOS 27 beta |
The networking runtime uses queue registration introduced in DriverKit 22.0 even though NetworkingDriverKit itself appeared earlier. Apple currently marks VideoDriverKit as beta; video generation requires an SDK containing that framework. See DriverKit and the family framework documentation for Apple’s platform availability.
For a local checkout, add SwifterKit to Package.swift:
let package = Package(
dependencies: [
.package(path: "../SwifterKit")
],
targets: [
.target(
name: "MyDriver",
dependencies: [
.product(name: "SwifterKit", package: "SwifterKit")
]
)
]
)Use a repository URL and a tagged version after publishing SwifterKit through a Swift package host.
A SwiftDriver declares static extension metadata and handles runtime work in Swift:
import SwifterKit
struct ExampleHIDDriver: SwiftDriver {
static let configuration = DriverConfiguration(
bundleIdentifier: "com.example.ExampleHID",
providerClass: "IOUserResources",
matchingProperties: ["IOResourceMatch": .string("IOKit")],
capabilities: .hid,
hidDevice: HIDDeviceConfiguration(
reportDescriptor: [0x06, 0x00, 0xFF, 0x09, 0x01, 0xA1, 0x01, 0xC0],
vendorID: 0x1234,
productID: 0x5678,
manufacturer: "Example",
product: "Swift HID",
serialNumber: "swift-hid-1",
primaryUsagePage: 0xFF00,
primaryUsage: 1
)
)
func start(context: DriverContext) async throws {
try await context.submitHIDInputReport(
HIDReport(bytes: [0], type: .input)
)
}
func handle(event: DriverEvent, context: DriverContext) async throws {
guard let report = try event.hidReport() else { return }
// Handle output and feature reports here.
_ = report
}
}Generate the internal extension project from the same configuration:
import Foundation
import SwifterKit
let outputDirectory = URL(fileURLWithPath: "/tmp/ExampleHID")
try DriverExtensionGenerator.generate(
configuration: ExampleHIDDriver.configuration,
at: outputDirectory
)The generator writes the personality, entitlements, runtime configuration, IIG declarations, native sources, and Xcode project. Generation rejects unsupported capability combinations and never overwrites an existing destination.
| Capability | Configuration | Swift operations |
|---|---|---|
| HID | HIDDeviceConfiguration |
Input reports; output and feature events |
| USB | USBDeviceConfiguration |
Control transfers, endpoint I/O, stall clearing, alternate settings |
| PCI | PCIDeviceConfiguration |
Configuration space, BAR access, device location, capability search |
| Serial | SerialPortConfiguration |
Queue I/O, modem state, receive errors, UART events |
| Block storage | BlockStorageDeviceConfiguration |
Eject, synchronize, unmap, read/write requests and completions |
| MIDI | MIDIDeviceConfiguration |
Endpoint topology, Universal MIDI Packet sends, destination events |
| Networking | EthernetDeviceConfiguration |
Packet queues, transmit completion, receive injection, link state |
| Audio | AudioDeviceConfiguration |
Stream rings, timestamps, formats, controls, custom properties |
| SCSI | SCSIControllerConfiguration or SCSIPeripheralConfiguration |
Parallel tasks, task management, CDBs, logical-unit services |
| Video | VideoDeviceConfiguration |
Formats, controls, buffers, queues, timestamps, stream events |
| Interrupts | InterruptSourceConfiguration |
Delivery control, interrupt metadata, typed events |
| Memory and DMA | MemoryPoolConfiguration |
Bounded buffers, valid lengths, provider mappings, DMA lifecycle |
A generated extension advertises only the capabilities implemented by its native runtime. Some device-family combinations are invalid because DriverKit requires different superclasses or providers.
See Capability APIs for configuration and completion details.
DriverExtensionGenerator copies a single packaged native source tree from Sources/SwifterKit/Resources/DriverKitExtension. Driver authors do not supply native glue to the generator.
The generated extension and the Swift host exchange versioned runtime messages. Swift receives typed values, opaque memory handles, and bounded payloads rather than DriverKit objects or native pointers. Ethernet, block-storage, SCSI, and other completion-based APIs require Swift to return the matching request identifier after transport work finishes.
DriverClient, DriverSession, and DriverCommand expose raw user-client calls for operations that do not belong to a typed capability API.
Read The native DriverKit boundary before adding raw commands or memory operations.
Run the complete validation suite:
./scripts/ci/validate.shThe suite checks Swift and C++ formatting, SwiftLint, Swift 6 tests, a release build with warnings as errors, DocC links, C++20 static analysis, unsigned arm64 and x86_64 DriverKit builds, property lists, and source LOC limits.
The compatibility job builds the package and its tests with Xcode 16.3 and a CI-only Swift 6.1 manifest header while retaining the macOS 10.15 deployment target. The main validation job uses the checked-in manifest and latest passing local toolchain.
CI does not run on macOS 10.15 and cannot activate a signed extension or attach physical hardware. The following macOS 10.15 runtime paths remain compile-checked but unexecuted in CI:
- IOKit service discovery and user-client opening through
kIOMasterPortDefault; - Swift concurrency back-deployment during the driver lifecycle and event loop;
- generated extension activation, runtime negotiation, and device I/O on a macOS 10.15 host; and
- signed entitlement, provisioning, and hardware behavior.
Unit tests force the legacy nanosecond sleep fallback and the modern duration-based branch independently. IOKit port selection depends on the running operating system and needs a macOS 10.15 host for runtime coverage.
For a smaller Swift-only cycle:
swift test -Xswiftc -warnings-as-errors
swiftlint lint --strict
xcrun swift-format lint --strict --recursive Sources Tests Package.swiftFormat edited source before submitting a change:
xcrun swift-format format --in-place --recursive Sources Tests Package.swift
xcrun clang-format -i Sources/SwifterKit/Resources/DriverKitExtension/Sources/*.{cpp,h,iig}Source and test files must remain below 800 lines after formatting; aim for 500. Directories below Tests/SwifterKitTests mirror the matching Sources/SwifterKit areas.
Generation reports that the destination exists. DriverExtensionGenerator does not overwrite files. Choose a new directory or remove the old output after confirming it is disposable.
A capability configuration is rejected. Check that RuntimeCapabilities, the matching configuration value, and the DriverKit provider class describe the same device family. Some families cannot share one generated superclass.
The DriverKit SDK cannot be found. Select an Xcode installation that includes DriverKit. CI sets DEVELOPER_DIR explicitly; local commands can do the same when multiple Xcode versions are installed.
A signed build fails. Confirm that the certificate, private key, provisioning profile, team identifier, bundle identifier, and approved entitlements agree. See Publishing for the local and GitHub Actions setup.
A source release does not need Apple signing. A downstream driver needs approved DriverKit entitlements, matching certificates and provisioning profiles, a host application, and the target device environment.
Publishing covers release tags, GitHub Actions, local .env files, signed validation, and Apple account requirements.
- Getting started with a Swift driver
- Capability APIs
- The native DriverKit boundary
- Publishing
- Changelog
Read CONTRIBUTING.md before opening a change.
Read AGENTS.md after this README. CLAUDE.md and GEMINI.md point to the same repository guidance.