Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

## Unreleased

### New features

- Add `Device.hasActionButton` and `Device.allDevicesWithActionButton` on iOS, including simulator detection, with support generated from GYB device parameters. ([#499](https://github.com/devicekit/DeviceKit/pull/499)) Thanks to [Simon Mau (@simonxmau)](https://github.com/simonxmau) for the original contribution!

### Maintenance

- Simplify GYB device definitions with named feature parameters and defaults for unsupported features, preserving the generated Swift output. ([#508](https://github.com/devicekit/DeviceKit/pull/508))
Expand Down
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ See our detailed [changelog](CHANGELOG.md) for the latest features, improvements
- [x] Detect available sensors (Touch ID, Face ID)
- [x] Detect available disk space
- [x] Apple Pencil support detection
- [x] Action Button detection

## Requirements

Expand Down Expand Up @@ -169,6 +170,14 @@ if device.isSimulator {
}
```

### Check if a device has an Action Button
```swift
let device = Device.current
if device.hasActionButton {
// Action Button specific behavior
}
```

### Get the Simulator Device
```swift
let device = Device.current
Expand Down
10 changes: 10 additions & 0 deletions Source/Device.generated.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1265,6 +1265,16 @@ public enum Device {
return isOneOf(Device.allDevicesWithDynamicIsland) || isOneOf(Device.allDevicesWithDynamicIsland.map(Device.simulator))
}

/// All devices that have an Action Button.
public static var allDevicesWithActionButton: [Device] {
return [.iPhone15Pro, .iPhone15ProMax, .iPhone16, .iPhone16Plus, .iPhone16Pro, .iPhone16ProMax, .iPhone16e, .iPhone17, .iPhone17Pro, .iPhone17ProMax, .iPhoneAir, .iPhone17e]
}

/// Returns whether or not the device has an Action Button.
public var hasActionButton: Bool {
return isOneOf(Device.allDevicesWithActionButton) || isOneOf(Device.allDevicesWithActionButton.map(Device.simulator))
}

/// All devices that have 3D Touch support.
public static var allDevicesWith3dTouchSupport: [Device] {
return [.iPhone6s, .iPhone6sPlus, .iPhone7, .iPhone7Plus, .iPhone8, .iPhone8Plus, .iPhoneX, .iPhoneXS, .iPhoneXSMax]
Expand Down
48 changes: 36 additions & 12 deletions Source/Device.swift.gyb
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ class Device:
supportsWirelessCharging=False,
hasRoundedDisplayCorners=False,
hasDynamicIsland=False,
hasActionButton=False,
applePencilSupport=0,
hasForce3dTouchSupport=False,
cameras=0,
Expand All @@ -50,6 +51,7 @@ class Device:
self.supportsWirelessCharging = supportsWirelessCharging
self.hasRoundedDisplayCorners = hasRoundedDisplayCorners
self.hasDynamicIsland = hasDynamicIsland
self.hasActionButton = hasActionButton
self.applePencilSupport = applePencilSupport
self.hasForce3dTouchSupport = hasForce3dTouchSupport
self.cameras = cameras
Expand Down Expand Up @@ -184,54 +186,66 @@ iPhones = [
Device("iPhone15Pro", "Device is an [iPhone 15 Pro](https://support.apple.com/en-us/111829)", "", ["iPhone16,1"], 6.1, (9, 19.5), "iPhone 15 Pro", "iPhone 15 Pro", 460,
cpu="a17Pro", isPro=True, isXSeries=True, hasFaceID=True, hasSensorHousing=True,
supportsWirelessCharging=True, hasRoundedDisplayCorners=True, hasDynamicIsland=True,
cameras=123, hasLidarSensor=True, hasUSBCConnectivity=True, has5gSupport=True),
cameras=123, hasLidarSensor=True, hasUSBCConnectivity=True, has5gSupport=True,
hasActionButton=True),
Device("iPhone15ProMax", "Device is an [iPhone 15 Pro Max](https://support.apple.com/en-us/111828)", "", ["iPhone16,2"], 6.7, (9, 19.5), "iPhone 15 Pro Max", "iPhone 15 Pro Max", 460,
cpu="a17Pro", isPlusFormFactor=True, isPro=True, isXSeries=True, hasFaceID=True,
hasSensorHousing=True, supportsWirelessCharging=True, hasRoundedDisplayCorners=True,
hasDynamicIsland=True, cameras=123, hasLidarSensor=True, hasUSBCConnectivity=True,
has5gSupport=True),
has5gSupport=True,
hasActionButton=True),
Device("iPhone16", "Device is an [iPhone 16]()", "", ["iPhone17,3"], 6.1, (9, 19.5), "iPhone 16", "iPhone 16", 460,
cpu="a18", isXSeries=True, hasFaceID=True, hasSensorHousing=True,
supportsWirelessCharging=True, hasRoundedDisplayCorners=True, hasDynamicIsland=True,
cameras=13, hasUSBCConnectivity=True, has5gSupport=True),
cameras=13, hasUSBCConnectivity=True, has5gSupport=True,
hasActionButton=True),
Device("iPhone16Plus", "Device is an [iPhone 16 Plus]()", "", ["iPhone17,4"], 6.7, (9, 19.5), "iPhone 16 Plus", "iPhone 16 Plus", 460,
cpu="a18", isPlusFormFactor=True, isXSeries=True, hasFaceID=True, hasSensorHousing=True,
supportsWirelessCharging=True, hasRoundedDisplayCorners=True, hasDynamicIsland=True,
cameras=13, hasUSBCConnectivity=True, has5gSupport=True),
cameras=13, hasUSBCConnectivity=True, has5gSupport=True,
hasActionButton=True),
Device("iPhone16Pro", "Device is an [iPhone 16 Pro]()", "", ["iPhone17,1"], 6.3, (9, 19.5), "iPhone 16 Pro", "iPhone 16 Pro", 460,
cpu="a18Pro", isPro=True, isXSeries=True, hasFaceID=True, hasSensorHousing=True,
supportsWirelessCharging=True, hasRoundedDisplayCorners=True, hasDynamicIsland=True,
cameras=123, hasLidarSensor=True, hasUSBCConnectivity=True, has5gSupport=True),
cameras=123, hasLidarSensor=True, hasUSBCConnectivity=True, has5gSupport=True,
hasActionButton=True),
Device("iPhone16ProMax", "Device is an [iPhone 16 Pro Max]()", "", ["iPhone17,2"], 6.9, (9, 19.5), "iPhone 16 Pro Max", "iPhone 16 Pro Max", 460,
cpu="a18Pro", isPlusFormFactor=True, isPro=True, isXSeries=True, hasFaceID=True,
hasSensorHousing=True, supportsWirelessCharging=True, hasRoundedDisplayCorners=True,
hasDynamicIsland=True, cameras=123, hasLidarSensor=True, hasUSBCConnectivity=True,
has5gSupport=True),
has5gSupport=True,
hasActionButton=True),
Device("iPhone16e", "Device is an [iPhone 16e](https://support.apple.com/en-us/122208)", "https://cdsassets.apple.com/live/7WUAS350/images/tech-specs/122208-iphone-16e.png", ["iPhone17,5"], 6.1, (9, 19.5), "iPhone 16e", "iPhone 16e", 460,
cpu="a18", isXSeries=True, hasFaceID=True, hasSensorHousing=True,
supportsWirelessCharging=True, hasRoundedDisplayCorners=True, cameras=1,
hasUSBCConnectivity=True, has5gSupport=True),
hasUSBCConnectivity=True, has5gSupport=True,
hasActionButton=True),
Device("iPhone17", "Device is an [iPhone 17]()", "", ["iPhone18,3"], 6.3, (9, 19.5), "iPhone 17", "iPhone 17", 460,
cpu="a19", isXSeries=True, hasFaceID=True, hasSensorHousing=True,
supportsWirelessCharging=True, hasRoundedDisplayCorners=True, hasDynamicIsland=True,
cameras=13, hasUSBCConnectivity=True, has5gSupport=True),
cameras=13, hasUSBCConnectivity=True, has5gSupport=True,
hasActionButton=True),
Device("iPhone17Pro", "Device is an [iPhone 17 Pro]()", "", ["iPhone18,1"], 6.3, (9, 19.5), "iPhone 17 Pro", "iPhone 17 Pro", 460,
cpu="a19Pro", isPro=True, isXSeries=True, hasFaceID=True, hasSensorHousing=True,
supportsWirelessCharging=True, hasRoundedDisplayCorners=True, hasDynamicIsland=True,
cameras=123, hasLidarSensor=True, hasUSBCConnectivity=True, has5gSupport=True),
cameras=123, hasLidarSensor=True, hasUSBCConnectivity=True, has5gSupport=True,
hasActionButton=True),
Device("iPhone17ProMax", "Device is an [iPhone 17 Pro Max]()", "", ["iPhone18,2"], 6.9, (9, 19.5), "iPhone 17 Pro Max", "iPhone 17 Pro Max", 460,
cpu="a19Pro", isPlusFormFactor=True, isPro=True, isXSeries=True, hasFaceID=True,
hasSensorHousing=True, supportsWirelessCharging=True, hasRoundedDisplayCorners=True,
hasDynamicIsland=True, cameras=123, hasLidarSensor=True, hasUSBCConnectivity=True,
has5gSupport=True),
has5gSupport=True,
hasActionButton=True),
Device("iPhoneAir", "Device is an [iPhone Air]()", "", ["iPhone18,4"], 6.5, (9, 19.5), "iPhone Air", "iPhone Air", 460,
cpu="a19Pro", isPlusFormFactor=True, isXSeries=True, hasFaceID=True, hasSensorHousing=True,
supportsWirelessCharging=True, hasRoundedDisplayCorners=True, hasDynamicIsland=True,
cameras=1, hasUSBCConnectivity=True, has5gSupport=True),
cameras=1, hasUSBCConnectivity=True, has5gSupport=True,
hasActionButton=True),
Device("iPhone17e", "Device is an [iPhone 17e]()", "", ["iPhone18,5"], 6.1, (9, 19.5), "iPhone 17e", "iPhone 17e", 460,
cpu="a19", isXSeries=True, hasFaceID=True, hasSensorHousing=True,
supportsWirelessCharging=True, hasRoundedDisplayCorners=True, cameras=1,
hasUSBCConnectivity=True, has5gSupport=True),
hasUSBCConnectivity=True, has5gSupport=True,
hasActionButton=True),
]

iPads = [
Expand Down Expand Up @@ -969,6 +983,16 @@ public enum Device {
return isOneOf(Device.allDevicesWithDynamicIsland) || isOneOf(Device.allDevicesWithDynamicIsland.map(Device.simulator))
}

/// All devices that have an Action Button.
public static var allDevicesWithActionButton: [Device] {
return [${', '.join(list(map(lambda device: "." + device.caseName, list(filter(lambda device: device.hasActionButton == True, iOSDevices)))))}]
}

/// Returns whether or not the device has an Action Button.
public var hasActionButton: Bool {
return isOneOf(Device.allDevicesWithActionButton) || isOneOf(Device.allDevicesWithActionButton.map(Device.simulator))
}

/// All devices that have 3D Touch support.
public static var allDevicesWith3dTouchSupport: [Device] {
return [${', '.join(list(map(lambda device: "." + device.caseName, list(filter(lambda device: device.hasForce3dTouchSupport == True, iOSDevices)))))}]
Expand Down
25 changes: 25 additions & 0 deletions Tests/Tests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -551,6 +551,31 @@ class DeviceKitTests: XCTestCase {
}
}

func testHasActionButton() {
let actionButtonDevices: [Device] = [
.iPhone15Pro,
.iPhone15ProMax,
.iPhone16,
.iPhone16Plus,
.iPhone16Pro,
.iPhone16ProMax,
.iPhone16e,
.iPhone17,
.iPhone17Pro,
.iPhone17ProMax,
.iPhoneAir,
.iPhone17e,
]
XCTAssertEqual(Device.allDevicesWithActionButton, actionButtonDevices)
for device in Device.allRealDevices {
let expected = device.isOneOf(actionButtonDevices)
XCTAssertEqual(device.hasActionButton, expected, "Action Button support for \(device)")
XCTAssertEqual(Device.simulator(device).hasActionButton, expected, "Simulator Action Button support for \(device)")
}
XCTAssertFalse(Device.unknown("unrecognized-device").hasActionButton)
XCTAssertFalse(Device.simulator(.unknown("unrecognized-device")).hasActionButton)
}

func testHas5gSupport() {
let has5gDevices: [Device] = [
.iPhone12,
Expand Down
Loading