Skip to content

Commit

Permalink
Merge branch '0.2.0' into main
Browse files Browse the repository at this point in the history
  • Loading branch information
Fernando Fernandes committed Oct 23, 2020
2 parents 3e058a2 + d7f3419 commit c8c7c9b
Show file tree
Hide file tree
Showing 4 changed files with 145 additions and 13 deletions.
22 changes: 19 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,17 @@
# GCOverseer
[Combine](https://developer.apple.com/documentation/combine) wrapper around Apple's [Game Controller Framework](https://developer.apple.com/documentation/gamecontroller).

## Tested platforms
## Tested Platforms
- iOS 14+
- macOS 11+ (BigSur+)
- Mac Catalyst 14+
- tvOS 14+
- Xcode 12+
- Swift 5.3+

## Usage
## Usage Examples

### Handle connected / disconnected events
### Handle Connected / Disconnected Events
```swift
import SpriteKit
import Combine
Expand All @@ -41,6 +41,22 @@ class GameScene: SKScene {
}
}
```
## Available Properties
Property | Description
-------- | -----------
`@Published var isGameControllerConnected` | Subscribe to this variable to keep track of connect / disconnect events of game controllers.

## Available APIs
API | Description
--- | -----------
`controllers()` | Returns all controllers that are connected to the device. E.g. *Dualshock*, *Xbox*, *Siri Remote* controllers, etc.
`extendedGamepadControllers()` | Returns all controllers supporting the `extendedGamepad` profile that are connected to the device. E.g. *Dualshock*, *Xbox* controllers, etc.
`dualshockControllers()` | Returns all *DualShock* controllers that are connected to the device.
`xboxControllers()` | Returns all *Xbox* controllers that are connected to the device.
`microGamepadControllers()` | Returns all controllers supporting the `microGamepad` profile that are connected to the device. E.g. Apple's *Siri Remote*.
`motionControllers()` | Returns all controllers supporting the `motion` profile that are connected to the device.
`controllerFor(playerIndex:)` | Returns the controller for the player 1, player 2, etc.
`enableLogging() / disableLogging()` | Enables / disables logging output.

## Integration
### Xcode
Expand Down
107 changes: 107 additions & 0 deletions Sources/GCOverseer/GCOverseer+Interface.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import GameController

/// Public APIs go here.
public extension GCOverseer {

/// Returns all controllers that are connected to the device. E.g. *Dualshock*, *Xbox*, *Siri Remote* controllers, etc.
///
/// The returned controllers support any gamepad profile: `extendedGamepad`, `microGamepad`, `motion`, etc.
///
/// - Returns: All controllers that are connected to the device.
func controllers() -> [GCController] {
let controllers = GCController.controllers()
log(information: "Number of connected controllers: \(controllers.count)", category: .controller)
if isLoggingEnabled {
controllers.enumerated().forEach {
let productCategoryPrefix = "Product category of controller \($0.offset + 1):"
let productCategory = String(describing: $0.element.productCategory) // E.g.: "Dualshock 4"
log(information: "\(productCategoryPrefix) \(productCategory)", category: .controller)
}
}
return controllers
}

/// Returns all controllers supporting the `extendedGamepad` profile that are connected to the device. E.g. *Dualshock*, *Xbox* controllers, etc.
///
/// The controls associated with the extended gamepad profile include the following:
/// - Two shoulder buttons.
/// - Two triggers.
/// - Four face buttons arranged in a diamond pattern.
/// - One directional pad.
/// - Two thumbsticks.
///
/// See: https://developer.apple.com/documentation/gamecontroller/gcextendedgamepad
///
/// Notice: the `gamepad` type is deprecated and it's now included in the `extendedGamepad` type, as
/// *... a controller supporting the Extended Gamepad profile for example supports the Gamepad profile and more...*.
///
/// - Returns: All the connected controllers supporting the `extendedGamepad` profile.
func extendedGamepadControllers() -> [GCController] {
let controllers = GCController.controllers().filter { $0.extendedGamepad != nil }
log(information: "Number of extended controllers: \(controllers.count)", category: .controller)
return controllers
}

/// Returns all *DualShock* controllers that are connected to the device.
///
/// - Returns: All the connected controllers with `physicalInputProfile` matching `GCDualShockGamepad`.
func dualshockControllers() -> [GCController] {
let controllers = GCController.controllers().filter {
$0.physicalInputProfile.isKind(of: GCDualShockGamepad.self)
}
log(information: "Number of Dualshock controllers: \(controllers.count)", category: .controller)
return controllers
}

/// Returns all *Xbox* controllers that are connected to the device.
///
/// - Returns: All the connected controllers with `physicalInputProfile` matching `GCXboxGamepad`.
func xboxControllers() -> [GCController] {
let controllers = GCController.controllers().filter { $0.physicalInputProfile.isKind(of: GCXboxGamepad.self) }
log(information: "Number of Xbox controllers: \(controllers.count)", category: .controller)
return controllers
}

/// Returns all controllers supporting the `microGamepad` profile that are connected to the device. E.g. Apple's *Siri Remote*.
///
/// The controls associated with the micro gamepad profile include the following:
/// - Two digital face buttons (A and X).
/// - One analog directional pad (D-pad), implemented as a touchpad.
///
/// Controllers that implement the micro gamepad profile can be used in either landscape or portrait orientations. By default, these devices are usually used in
/// portrait mode.
///
/// See: https://developer.apple.com/documentation/gamecontroller/gcmicrogamepad
///
/// - Returns: All the connected controllers supporting the `microGamepad` profile.
func microGamepadControllers() -> [GCController] {
let controllers = GCController.controllers().filter { $0.microGamepad != nil }
log(information: "Number of micro controllers: \(controllers.count)", category: .controller)
return controllers
}

/// Returns all controllers supporting the `motion` profile that are connected to the device.
///
/// The `motion` profile provides information about the orientation and motion of the controller.
///
/// See: https://developer.apple.com/documentation/gamecontroller/gcmotion
///
/// - Returns: All the connected controllers supporting the `microGamepad` profile.
func motionControllers() -> [GCController] {
let controllers = GCController.controllers().filter { $0.motion != nil }
log(information: "Number of motion controllers: \(controllers.count)", category: .controller)
return controllers
}

/// Returns the controller for the player 1, player 2, etc.
///
/// - Parameter playerIndex: The player number that may have a controller associated with. E.g.: `.index1`
/// - Returns: An optional `GCController` associated with the given player number (`GCControllerPlayerIndex`).
func controllerFor(playerIndex: GCControllerPlayerIndex) -> GCController? {
let controller = GCController.controllers().first(where: { $0.playerIndex == playerIndex })
let controllerInfo = String(describing: controller)
let playerNumber = playerIndex.rawValue + 1
log(information: "Player \(playerNumber) controller: \(controllerInfo)", category: .controller)
return controller
}
}
13 changes: 12 additions & 1 deletion Sources/GCOverseer/GCOverseer+Logging.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,16 @@ import AppLogger
///
/// Refer to: https://developer.apple.com/documentation/os/logging
public enum GCOverseerLoggingCategory: String {
case gcNotification = "GCNotification"
case gcNotification = "GCO_Notification"
case controller = "GCO_Controller"
}

// MARK: - Interface

public extension GCOverseer {

// MARK: Enable / Disable Logging

/// Enables logging information via `AppLogger`.
///
/// When logging is enabled, the output will be available in *Xcode's Console* or
Expand All @@ -27,7 +32,13 @@ public extension GCOverseer {
}
}

// MARK: - Internal

internal extension GCOverseer {

func log(notification: Notification) {
log(information: "Received game controller notification: \(notification)", category: .gcNotification)
}

/// Logs the given `String` information via `AppLogger`.
///
Expand Down
16 changes: 7 additions & 9 deletions Sources/GCOverseer/GCOverseer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,15 @@ public class GCOverseer: ObservableObject {

// MARK: - Properties

/// Subscribe to this variable to keep track of connect / disconnect events of game controllers.
@Published public var isGameControllerConnected: Bool = false

var isLoggingEnabled: Bool = true
// MARK: Internal Properties

// MARK: - Private Properties
/// Enables / disables logging output to both *Xcode's Console* and the macOS *Console app*. `true` by default.
internal var isLoggingEnabled: Bool = true

// MARK: Private Properties

private var cancellableNotifications = Set<AnyCancellable>()

Expand Down Expand Up @@ -41,16 +45,10 @@ private extension GCOverseer {
let didConnect = (notificationName == .GCControllerDidConnect)
notificationCenter
.publisher(for: notificationName)
.handleEvents(receiveOutput: { self.log(notification: $0) })
.handleEvents(receiveOutput: { [weak self] in self?.log(notification: $0) })
.receive(on: DispatchQueue.main)
.map({ _ in didConnect })
.assign(to: \.isGameControllerConnected, on: self)
.store(in: &cancellableNotifications)
}

// MARK: Logging

func log(notification: Notification) {
log(information: "Received game controller notification: \(notification)", category: .gcNotification)
}
}

0 comments on commit c8c7c9b

Please sign in to comment.