mirror of
https://github.com/devicekit/DeviceKit.git
synced 2026-05-17 10:20:34 +00:00
Add Apple Vision Pro (M2) and Apple Vision Pro (M5) support
Implement full visionOS device support with both Vision Pro models (identifiers RealityDevice14,1 and RealityDevice17,1). Replace all visionOS TODO placeholders with proper implementations including device enum cases, identifier mapping, property getters (description, safeDescription, cpu), and static device arrays. Add comprehensive unit tests for visionOS platform. Enable visionOS simulator testing in GitHub Actions CI pipeline. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -43,6 +43,7 @@ jobs:
|
||||
- platform=tvOS Simulator,name=Apple TV
|
||||
- platform=tvOS Simulator,name=Apple TV 4K (3rd generation)
|
||||
- platform=watchOS Simulator,name=Apple Watch Series 10 (46mm)
|
||||
- platform=visionOS Simulator,name=Apple Vision Pro
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
- Add support for M5 iPad Pro models. ([#467](https://github.com/devicekit/DeviceKit/pull/467))
|
||||
- Add support for Apple Watch SE (3rd generation) ([#473](https://github.com/devicekit/DeviceKit/pull/473))
|
||||
- Add support for iPhone 17e and iPad Air (M4)
|
||||
- Add support for Apple Vision Pro and Apple Vision Pro (M5)
|
||||
|
||||
| Device | Case value |
|
||||
| --- | --- |
|
||||
@@ -17,6 +18,8 @@
|
||||
| iPhone 17e | `Device.iPhone17e` |
|
||||
| iPad Air (11-inch) (M4) | `Device.iPadAir11M4` |
|
||||
| iPad Air (13-inch) (M4) | `Device.iPadAir13M4` |
|
||||
| Apple Vision Pro | `Device.appleVisionPro` |
|
||||
| Apple Vision Pro (M5) | `Device.appleVisionProM5` |
|
||||
|
||||
### Bug fixes
|
||||
|
||||
|
||||
@@ -588,6 +588,15 @@ public enum Device {
|
||||
///
|
||||
/// ![Image]()
|
||||
case appleWatchSeries11_46mm
|
||||
#elseif os(visionOS)
|
||||
/// Device is an [Apple Vision Pro](https://support.apple.com/kb/SP911)
|
||||
///
|
||||
/// ![Image]()
|
||||
case appleVisionPro
|
||||
/// Device is an [Apple Vision Pro (M5)](https://support.apple.com/en-us/125436)
|
||||
///
|
||||
/// ![Image]()
|
||||
case appleVisionProM5
|
||||
#endif
|
||||
|
||||
/// Device is [Simulator](https://developer.apple.com/library/ios/documentation/IDEs/Conceptual/iOS_Simulator_Guide/Introduction/Introduction.html)
|
||||
@@ -774,8 +783,12 @@ public enum Device {
|
||||
default: return unknown(identifier)
|
||||
}
|
||||
#elseif os(visionOS)
|
||||
// TODO: Replace with proper implementation for visionOS.
|
||||
return unknown(identifier)
|
||||
switch identifier {
|
||||
case "RealityDevice14,1": return appleVisionPro
|
||||
case "RealityDevice17,1": return appleVisionProM5
|
||||
case "i386", "x86_64", "arm64": return simulator(mapToDevice(identifier: ProcessInfo().environment["SIMULATOR_MODEL_IDENTIFIER"] ?? "visionOS"))
|
||||
default: return unknown(identifier)
|
||||
}
|
||||
#else
|
||||
return unknown(identifier)
|
||||
#endif
|
||||
@@ -1344,6 +1357,16 @@ public enum Device {
|
||||
public var hasForceTouchSupport: Bool {
|
||||
return isOneOf(Device.allWatchesWithForceTouchSupport) || isOneOf(Device.allWatchesWithForceTouchSupport.map(Device.simulator))
|
||||
}
|
||||
#elseif os(visionOS)
|
||||
/// All Vision devices
|
||||
public static var allVisions: [Device] {
|
||||
return [.appleVisionPro, .appleVisionProM5]
|
||||
}
|
||||
|
||||
/// All simulator Vision devices
|
||||
public static var allSimulatorVisions: [Device] {
|
||||
return allVisions.map(Device.simulator)
|
||||
}
|
||||
#endif
|
||||
|
||||
/// Returns whether the current device is a SwiftUI preview canvas
|
||||
@@ -1365,8 +1388,7 @@ public enum Device {
|
||||
#elseif os(watchOS)
|
||||
return allWatches
|
||||
#elseif os(visionOS)
|
||||
// TODO: Replace with proper implementation for visionOS.
|
||||
return []
|
||||
return allVisions
|
||||
#else
|
||||
return []
|
||||
#endif
|
||||
@@ -1636,7 +1658,6 @@ public enum Device {
|
||||
#elseif os(tvOS)
|
||||
return nil
|
||||
#elseif os(visionOS)
|
||||
// TODO: Replace with proper implementation for visionOS.
|
||||
return nil
|
||||
#else
|
||||
return nil
|
||||
@@ -1821,8 +1842,12 @@ extension Device: CustomStringConvertible {
|
||||
case .unknown(let identifier): return identifier
|
||||
}
|
||||
#elseif os(visionOS)
|
||||
// TODO: Replace with proper implementation for visionOS.
|
||||
return "Apple Vision Pro"
|
||||
switch self {
|
||||
case .appleVisionPro: return "Apple Vision Pro"
|
||||
case .appleVisionProM5: return "Apple Vision Pro (M5)"
|
||||
case .simulator(let model): return "Simulator (\(model.description))"
|
||||
case .unknown(let identifier): return identifier
|
||||
}
|
||||
#else
|
||||
switch self {
|
||||
case .simulator(let model): return "Simulator (\(model.description))"
|
||||
@@ -1986,8 +2011,12 @@ extension Device: CustomStringConvertible {
|
||||
case .unknown(let identifier): return identifier
|
||||
}
|
||||
#elseif os(visionOS)
|
||||
// TODO: Replace with proper implementation for visionOS.
|
||||
return "Apple Vision Pro"
|
||||
switch self {
|
||||
case .appleVisionPro: return "Apple Vision Pro"
|
||||
case .appleVisionProM5: return "Apple Vision Pro (M5)"
|
||||
case .simulator(let model): return "Simulator (\(model.safeDescription))"
|
||||
case .unknown(let identifier): return identifier
|
||||
}
|
||||
#else
|
||||
switch self {
|
||||
case .simulator(let model): return "Simulator (\(model.safeDescription))"
|
||||
@@ -2508,7 +2537,7 @@ extension Device {
|
||||
extension Device {
|
||||
|
||||
public enum CPU: Comparable {
|
||||
#if os(iOS) || os(tvOS)
|
||||
#if os(iOS) || os(tvOS) || os(visionOS)
|
||||
case a4
|
||||
case a5
|
||||
case a5X
|
||||
@@ -2707,8 +2736,12 @@ extension Device {
|
||||
case .unknown: return .unknown
|
||||
}
|
||||
#elseif os(visionOS)
|
||||
// TODO: Replace with proper implementation for visionOS.
|
||||
return .unknown
|
||||
switch self {
|
||||
case .appleVisionPro: return .m2
|
||||
case .appleVisionProM5: return .m5
|
||||
case .simulator(let model): return model.cpu
|
||||
case .unknown: return .unknown
|
||||
}
|
||||
#else
|
||||
return .unknown
|
||||
#endif
|
||||
@@ -2719,7 +2752,7 @@ extension Device.CPU: CustomStringConvertible {
|
||||
|
||||
/// A textual representation of the device.
|
||||
public var description: String {
|
||||
#if os(iOS) || os(tvOS)
|
||||
#if os(iOS) || os(tvOS) || os(visionOS)
|
||||
switch self {
|
||||
case .a4: return "A4"
|
||||
case .a5: return "A5"
|
||||
@@ -2768,9 +2801,6 @@ extension Device.CPU: CustomStringConvertible {
|
||||
case .s10: return "S10"
|
||||
case .unknown: return "unknown"
|
||||
}
|
||||
#elseif os(visionOS)
|
||||
// TODO: Replace with proper implementation for visionOS.
|
||||
return "unknown"
|
||||
#else
|
||||
return "unknown"
|
||||
#endif
|
||||
|
||||
+55
-16
@@ -156,6 +156,12 @@ tvs = [
|
||||
Device("appleTV4K3", "Device is an [Apple TV 4K (3rd generation)](https://support.apple.com/kb/SP886)", "https://support.apple.com/library/APPLE/APPLECARE_ALLGEOS/SP886/apple-tv-4k-3gen_2x.png", ["AppleTV14,1"], 0, (), "Apple TV 4K (3rd generation)", "Apple TV 4K (3rd generation)", -1, False, False, False, False, False, False, False, False, False, False, 0, False, 0, False, "a15Bionic", False, False),
|
||||
]
|
||||
|
||||
# visionOS
|
||||
visions = [
|
||||
Device("appleVisionPro", "Device is an [Apple Vision Pro](https://support.apple.com/kb/SP911)", "", ["RealityDevice14,1"], 0, (1, 1), "Apple Vision Pro", "Apple Vision Pro", -1, False, False, False, False, False, False, False, False, False, False, 0, False, 0, True, "m2", True, False),
|
||||
Device("appleVisionProM5", "Device is an [Apple Vision Pro (M5)](https://support.apple.com/en-us/125436)", "", ["RealityDevice17,1"], 0, (1, 1), "Apple Vision Pro (M5)", "Apple Vision Pro (M5)", -1, False, False, False, False, False, False, False, False, False, False, 0, False, 0, True, "m5", True, False),
|
||||
]
|
||||
|
||||
# watchOS
|
||||
watches = [
|
||||
Device(
|
||||
@@ -361,6 +367,7 @@ watches = [
|
||||
iOSDevices = iPods + iPhones + iPads + homePods
|
||||
tvOSDevices = tvs
|
||||
watchOSDevices = watches
|
||||
visionOSDevices = visions
|
||||
}%
|
||||
#if os(watchOS)
|
||||
import WatchKit
|
||||
@@ -422,6 +429,13 @@ public enum Device {
|
||||
///
|
||||
/// 
|
||||
case ${device.caseName}
|
||||
% end
|
||||
#elseif os(visionOS)
|
||||
% for device in visionOSDevices:
|
||||
/// ${device.comment}
|
||||
///
|
||||
/// 
|
||||
case ${device.caseName}
|
||||
% end
|
||||
#endif
|
||||
|
||||
@@ -484,8 +498,13 @@ public enum Device {
|
||||
default: return unknown(identifier)
|
||||
}
|
||||
#elseif os(visionOS)
|
||||
// TODO: Replace with proper implementation for visionOS.
|
||||
return unknown(identifier)
|
||||
switch identifier {
|
||||
% for device in visionOSDevices:
|
||||
case ${', '.join(list(map(lambda device: "\"" + device + "\"", device.identifiers)))}: return ${device.caseName}
|
||||
% end
|
||||
case "i386", "x86_64", "arm64": return simulator(mapToDevice(identifier: ProcessInfo().environment["SIMULATOR_MODEL_IDENTIFIER"] ?? "visionOS"))
|
||||
default: return unknown(identifier)
|
||||
}
|
||||
#else
|
||||
return unknown(identifier)
|
||||
#endif
|
||||
@@ -806,6 +825,16 @@ public enum Device {
|
||||
public var hasForceTouchSupport: Bool {
|
||||
return isOneOf(Device.allWatchesWithForceTouchSupport) || isOneOf(Device.allWatchesWithForceTouchSupport.map(Device.simulator))
|
||||
}
|
||||
#elseif os(visionOS)
|
||||
/// All Vision devices
|
||||
public static var allVisions: [Device] {
|
||||
return [${', '.join(list(map(lambda device: "." + device.caseName, visionOSDevices)))}]
|
||||
}
|
||||
|
||||
/// All simulator Vision devices
|
||||
public static var allSimulatorVisions: [Device] {
|
||||
return allVisions.map(Device.simulator)
|
||||
}
|
||||
#endif
|
||||
|
||||
/// Returns whether the current device is a SwiftUI preview canvas
|
||||
@@ -827,8 +856,7 @@ public enum Device {
|
||||
#elseif os(watchOS)
|
||||
return allWatches
|
||||
#elseif os(visionOS)
|
||||
// TODO: Replace with proper implementation for visionOS.
|
||||
return []
|
||||
return allVisions
|
||||
#else
|
||||
return []
|
||||
#endif
|
||||
@@ -974,7 +1002,6 @@ public enum Device {
|
||||
#elseif os(tvOS)
|
||||
return nil
|
||||
#elseif os(visionOS)
|
||||
// TODO: Replace with proper implementation for visionOS.
|
||||
return nil
|
||||
#else
|
||||
return nil
|
||||
@@ -1034,8 +1061,13 @@ extension Device: CustomStringConvertible {
|
||||
case .unknown(let identifier): return identifier
|
||||
}
|
||||
#elseif os(visionOS)
|
||||
// TODO: Replace with proper implementation for visionOS.
|
||||
return "Apple Vision Pro"
|
||||
switch self {
|
||||
% for device in visionOSDevices:
|
||||
case .${device.caseName}: return "${device.description}"
|
||||
% end
|
||||
case .simulator(let model): return "Simulator (\(model.description))"
|
||||
case .unknown(let identifier): return identifier
|
||||
}
|
||||
#else
|
||||
switch self {
|
||||
case .simulator(let model): return "Simulator (\(model.description))"
|
||||
@@ -1074,8 +1106,13 @@ extension Device: CustomStringConvertible {
|
||||
case .unknown(let identifier): return identifier
|
||||
}
|
||||
#elseif os(visionOS)
|
||||
// TODO: Replace with proper implementation for visionOS.
|
||||
return "Apple Vision Pro"
|
||||
switch self {
|
||||
% for device in visionOSDevices:
|
||||
case .${device.caseName}: return "${device.safeDescription}"
|
||||
% end
|
||||
case .simulator(let model): return "Simulator (\(model.safeDescription))"
|
||||
case .unknown(let identifier): return identifier
|
||||
}
|
||||
#else
|
||||
switch self {
|
||||
case .simulator(let model): return "Simulator (\(model.safeDescription))"
|
||||
@@ -1568,7 +1605,7 @@ watchOS_cpus = [
|
||||
extension Device {
|
||||
|
||||
public enum CPU: Comparable {
|
||||
#if os(iOS) || os(tvOS)
|
||||
#if os(iOS) || os(tvOS) || os(visionOS)
|
||||
% for cpu in iOS_cpus:
|
||||
case ${cpu.name}
|
||||
% end
|
||||
@@ -1607,8 +1644,13 @@ extension Device {
|
||||
case .unknown: return .unknown
|
||||
}
|
||||
#elseif os(visionOS)
|
||||
// TODO: Replace with proper implementation for visionOS.
|
||||
return .unknown
|
||||
switch self {
|
||||
% for device in visionOSDevices:
|
||||
case .${device.caseName}: return .${device.cpu}
|
||||
% end
|
||||
case .simulator(let model): return model.cpu
|
||||
case .unknown: return .unknown
|
||||
}
|
||||
#else
|
||||
return .unknown
|
||||
#endif
|
||||
@@ -1619,7 +1661,7 @@ extension Device.CPU: CustomStringConvertible {
|
||||
|
||||
/// A textual representation of the device.
|
||||
public var description: String {
|
||||
#if os(iOS) || os(tvOS)
|
||||
#if os(iOS) || os(tvOS) || os(visionOS)
|
||||
switch self {
|
||||
% for cpu in iOS_cpus:
|
||||
case .${cpu.name}: return "${cpu.description}"
|
||||
@@ -1633,9 +1675,6 @@ extension Device.CPU: CustomStringConvertible {
|
||||
% end
|
||||
case .unknown: return "unknown"
|
||||
}
|
||||
#elseif os(visionOS)
|
||||
// TODO: Replace with proper implementation for visionOS.
|
||||
return "unknown"
|
||||
#else
|
||||
return "unknown"
|
||||
#endif
|
||||
|
||||
@@ -819,4 +819,30 @@ class DeviceKitTests: XCTestCase {
|
||||
|
||||
#endif
|
||||
|
||||
// MARK: - visionOS
|
||||
#if os(visionOS)
|
||||
|
||||
func testMapFromIdentifier() {
|
||||
XCTAssertEqual(Device.mapToDevice(identifier: "RealityDevice14,1"), .appleVisionPro)
|
||||
XCTAssertEqual(Device.mapToDevice(identifier: "RealityDevice17,1"), .appleVisionProM5)
|
||||
}
|
||||
|
||||
func testDeviceCPU() {
|
||||
XCTAssertEqual(Device.appleVisionPro.cpu, Device.CPU.m2)
|
||||
XCTAssertEqual(Device.appleVisionProM5.cpu, Device.CPU.m5)
|
||||
}
|
||||
|
||||
func testDescription() {
|
||||
XCTAssertEqual(Device.appleVisionPro.description, "Apple Vision Pro")
|
||||
XCTAssertEqual(Device.appleVisionProM5.description, "Apple Vision Pro (M5)")
|
||||
}
|
||||
|
||||
func testSafeDescription() {
|
||||
for device in Device.allRealDevices {
|
||||
XCTAssertEqual(device.description, device.safeDescription)
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user