McNight/padawan-kit

A framework for Controllers

★ 3Forks 0SwiftGitHub ↗Compare

README

PadawanKit

PadawanKit is a Swift-native, concurrency-first interface to Apple's GameController framework. Lifecycle changes are explicit, controller input is exposed through shared AsyncSequence values, and the recommended API avoids GameController types.

It requires Swift 6.2, macOS 15, iOS 18, tvOS 18, or visionOS 2.

Read the API documentation · View PadawanKit on Swift Package Index

Products

  • PadawanKit: lifecycle, input, state, capabilities, discovery, recognition, haptics, and supported peripherals.
  • PadawanUI: environment integration and view modifiers.

PadawanInterop remains an implementation target, not a published library product.

Start and discover

import PadawanKit

@MainActor
func run() async {
    let central = Central()
    central.start()
    defer { central.stop() }

    for await event in central.events {
        if case let .connected(controller) = event {
            print(controller.metadata, controller.capabilityCatalog)
        }
    }
}

Creating a Central has no side effects. Call start(), stop(), startDiscovery(), and stopDiscovery() explicitly.

Input and state

for await input in controller.inputEvents {
    // System controller mappings are applied.
}

let current = controller.inputSnapshot()
let physical = controller.inputSnapshot(mapping: .unmapped)

Each shared input stream retains its newest 64 pending events. A subscriber that cannot keep up can miss intermediate button transitions as well as analog samples. Drain events promptly into storage you own when lossless history is required, or use a snapshot when only current state matters.

Recognition

for await _ in controller.sequenceEvents(for: .konamiCode) {
    activateCheatMode()
}

let actions = ControllerActionMap(bindings: [
    .init(action: "jump", gesture: .press(.a)),
    .init(action: "sprint", gesture: .value(.leftTrigger, minimumValue: 0.8)),
])

for await action in controller.actionEvents(for: actions) {
    perform(action)
}

Haptics

let heartbeat: HapticPattern = [
    .transient(intensity: 1, sharpness: 0.7),
    .transient(intensity: 0.7, sharpness: 0.5, at: 0.14),
]

try controller.haptics?.start()
try controller.haptics?.play(heartbeat)

Haptic and DualSense adaptive-trigger APIs use typed errors. Haptic patterns are Codable and can create retained players for looping, seeking, pausing, and live parameter changes.

SwiftUI

ContentView()
    .controllerCentral(central)
    .onControllerAvailable(of: DualSenseController.self) { controller in
        // Existing and newly connected DualSense controllers.
    }
    .onControllerInput(.a) { controller, input in
        // Handle input while this view is active.
    }
    .controllerConnectionNotice(.hud)

The SwiftUI layer consumes an already-started Central; view construction does not start controller observation or discovery.

Additional devices

PadawanKit includes explicitly managed keyboard, mouse, racing-wheel, iOS virtual-controller, and visionOS 26 spatial-stylus APIs. Unknown controllers remain usable through the generic capability model.

Command-line tool

The independently buildable command-line package lives in the padawan-cli repository. It depends on this package and contains padawan, its bridge tests, and the browser/Wasm experiment.

Development

Run these commands from the repository root:

swift build -Xswiftc -warnings-as-errors
swift test -Xswiftc -warnings-as-errors

Build DocC with:

xcodebuild docbuild -scheme PadawanKit -destination 'generic/platform=macOS'
xcodebuild docbuild -scheme PadawanUI -destination 'generic/platform=macOS'

See the DocC guides for lifecycle, buffering, recorded-input testing, and the manual hardware verification matrix.

License

PadawanKit is available under the MIT License.

Contributors

McNight

Issues