From 97fb184706a8fd9de96e2a2702b934028572893e Mon Sep 17 00:00:00 2001 From: Zandor Smith Date: Mon, 21 Sep 2026 11:07:45 +0200 Subject: [PATCH 1/2] Add iPhone 18 Pro and Apple Watch Series 12 and Ultra 4 support Adapt PR #507, excluding iPhone Duo and the foldability API. Replace provisional identifiers with researched hardware mappings, including both iPhone 18 Pro Max regional variants. Add Apple technical specification links, display metrics, and device mapping regression coverage. Adapted from commit fe2c427e0d15f7669ca5185723f2b40cae3914f0. --- CHANGELOG.md | 7 ++ Source/Device.generated.swift | 143 +++++++++++++++++----------------- Source/Device.swift.gyb | 30 +++++++ Tests/Tests.swift | 70 +++++++++++++++++ 4 files changed, 178 insertions(+), 72 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0a3305fb..d99894f8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,12 @@ ## Unreleased +### New devices + +- Add iPhone 18 Pro, iPhone 18 Pro Max, Apple Watch Series 12 (42mm/46mm), and Apple Watch Ultra 4 support, including A20 Pro and S11 chips. Adapted from [#507](https://github.com/devicekit/DeviceKit/pull/507), excluding iPhone Duo. +- Add hardware identifier mappings, including both regional iPhone 18 Pro Max variants and GPS/cellular Series 12 variants. +- Add Apple technical specification links and verified display metrics; Watch display diagonals are calculated from the published pixel dimensions and PPI. + ### New features - Add `allDevicesWithEsimSupport` / `hasEsimSupport` and `allDevicesWithDualEsimSupport` / `hasDualEsimSupport`, including simulator support. Cover supported iPhones through iPhone 17e and iPads through iPad Air (M4) and iPad Pro (M5), exclude iPhone X, and document regional and Wi-Fi + Cellular limitations. ([#382](https://github.com/devicekit/DeviceKit/pull/382)) @@ -10,6 +16,7 @@ - Update Apple support links for iPhone and iPad models, correct the iPhone 14 Pro Max pixel density to 460 ppi, and correct the iPad Air 11-inch (M2) aspect ratio to 41:59. ([#497](https://github.com/devicekit/DeviceKit/pull/497)) + ### 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)) diff --git a/Source/Device.generated.swift b/Source/Device.generated.swift index 4aa90682..1fc55345 100644 --- a/Source/Device.generated.swift +++ b/Source/Device.generated.swift @@ -203,61 +203,39 @@ public enum Device { /// ![Image](https://support.apple.com/library/APPLE/APPLECARE_ALLGEOS/SP875/sp875-sp876-iphone14-pro-promax_2x.png) case iPhone14ProMax /// Device is an [iPhone 15](https://support.apple.com/kb/SP901) - /// - /// ![Image]() case iPhone15 /// Device is an [iPhone 15 Plus](https://support.apple.com/kb/SP902) - /// - /// ![Image]() case iPhone15Plus /// Device is an [iPhone 15 Pro](https://support.apple.com/kb/SP903) - /// - /// ![Image]() case iPhone15Pro /// Device is an [iPhone 15 Pro Max](https://support.apple.com/kb/SP904) - /// - /// ![Image]() case iPhone15ProMax /// Device is an [iPhone 16](https://support.apple.com/121029) - /// - /// ![Image]() case iPhone16 /// Device is an [iPhone 16 Plus](https://support.apple.com/121030) - /// - /// ![Image]() case iPhone16Plus /// Device is an [iPhone 16 Pro](https://support.apple.com/121031) - /// - /// ![Image]() case iPhone16Pro /// Device is an [iPhone 16 Pro Max](https://support.apple.com/121032) - /// - /// ![Image]() case iPhone16ProMax /// Device is an [iPhone 16e](https://support.apple.com/122208) /// /// ![Image](https://cdsassets.apple.com/live/7WUAS350/images/tech-specs/122208-iphone-16e.png) case iPhone16e /// Device is an [iPhone 17](https://support.apple.com/125089) - /// - /// ![Image]() case iPhone17 /// Device is an [iPhone 17 Pro](https://support.apple.com/125090) - /// - /// ![Image]() case iPhone17Pro /// Device is an [iPhone 17 Pro Max](https://support.apple.com/125091) - /// - /// ![Image]() case iPhone17ProMax /// Device is an [iPhone Air](https://support.apple.com/125092) - /// - /// ![Image]() case iPhoneAir /// Device is an [iPhone 17e](https://support.apple.com/126470) - /// - /// ![Image]() case iPhone17e + /// Device is an [iPhone 18 Pro](https://support.apple.com/en-us/148590) + case iPhone18Pro + /// Device is an [iPhone 18 Pro Max](https://support.apple.com/en-us/148591) + case iPhone18ProMax /// Device is an [iPad 2](https://support.apple.com/kb/SP622) /// /// ![Image](https://support.apple.com/library/APPLE/APPLECARE_ALLGEOS/SP622/SP622_01-ipad2-mul.png) @@ -307,8 +285,6 @@ public enum Device { /// ![Image](https://support.apple.com/library/APPLE/APPLECARE_ALLGEOS/SP884/sp884-ipad-10gen-960_2x.png) case iPad10 /// Device is an [iPad (A16)](https://support.apple.com/122240) - /// - /// ![Image]() case iPadA16 /// Device is an [iPad Air (4th generation)](https://support.apple.com/kb/SP828) /// @@ -335,12 +311,8 @@ public enum Device { /// ![Image](https://cdsassets.apple.com/content/services/pub/image?productid=301029&size=240x240) case iPadAir13M3 /// Device is an [iPad Air 11-inch (M4)](https://support.apple.com/126471) - /// - /// ![Image]() case iPadAir11M4 /// Device is an [iPad Air 13-inch (M4)](https://support.apple.com/126472) - /// - /// ![Image]() case iPadAir13M4 /// Device is an [iPad mini](https://support.apple.com/kb/SP661) /// @@ -549,45 +521,31 @@ public enum Device { /// ![Image](https://support.apple.com/library/APPLE/APPLECARE_ALLGEOS/SP879/apple-watch-ultra_2x.png) case appleWatchUltra /// Device is an [Apple Watch Series 9](https://support.apple.com/kb/SP905) - /// - /// ![Image]() case appleWatchSeries9_41mm /// Device is an [Apple Watch Series 9](https://support.apple.com/kb/SP905) - /// - /// ![Image]() case appleWatchSeries9_45mm /// Device is an [Apple Watch Ultra2]() - /// - /// ![Image]() case appleWatchUltra2 /// Device is an [Apple Watch SE (3rd generation)]() - /// - /// ![Image]() case appleWatchSE3_40mm /// Device is an [Apple Watch SE (3rd generation)]() - /// - /// ![Image]() case appleWatchSE3_44mm /// Device is an [Apple Watch Series 10]() - /// - /// ![Image]() case appleWatchSeries10_42mm /// Device is an [Apple Watch Series 10]() - /// - /// ![Image]() case appleWatchSeries10_46mm /// Device is an [Apple Watch Ultra 3]() - /// - /// ![Image]() case appleWatchUltra3 /// Device is an [Apple Watch Series 11]() - /// - /// ![Image]() case appleWatchSeries11_42mm /// Device is an [Apple Watch Series 11]() - /// - /// ![Image]() case appleWatchSeries11_46mm + /// Device is an [Apple Watch Series 12 42mm](https://support.apple.com/en-us/148589) + case appleWatchSeries12_42mm + /// Device is an [Apple Watch Series 12 46mm](https://support.apple.com/en-us/148589) + case appleWatchSeries12_46mm + /// Device is an [Apple Watch Ultra 4](https://support.apple.com/en-us/148758) + case appleWatchUltra4 #endif /// Device is [Simulator](https://developer.apple.com/library/ios/documentation/IDEs/Conceptual/iOS_Simulator_Guide/Introduction/Introduction.html) @@ -678,6 +636,8 @@ public enum Device { case "iPhone18,2": return iPhone17ProMax case "iPhone18,4": return iPhoneAir case "iPhone18,5": return iPhone17e + case "iPhone19,2": return iPhone18Pro + case "iPhone19,3", "iPhone19,7": return iPhone18ProMax case "iPad2,1", "iPad2,2", "iPad2,3", "iPad2,4": return iPad2 case "iPad3,1", "iPad3,2", "iPad3,3": return iPad3 case "iPad3,4", "iPad3,5", "iPad3,6": return iPad4 @@ -770,6 +730,9 @@ public enum Device { case "Watch7,12": return appleWatchUltra3 case "Watch7,17", "Watch7,19": return appleWatchSeries11_42mm case "Watch7,18", "Watch7,20": return appleWatchSeries11_46mm + case "Watch8,2", "Watch8,4": return appleWatchSeries12_42mm + case "Watch8,3", "Watch8,5": return appleWatchSeries12_46mm + case "Watch8,1": return appleWatchUltra4 case "i386", "x86_64", "arm64": return simulator(mapToDevice(identifier: ProcessInfo().environment["SIMULATOR_MODEL_IDENTIFIER"] ?? "watchOS")) default: return unknown(identifier) } @@ -853,6 +816,8 @@ public enum Device { case .iPhone17ProMax: return 6.9 case .iPhoneAir: return 6.5 case .iPhone17e: return 6.1 + case .iPhone18Pro: return 6.3 + case .iPhone18ProMax: return 6.9 case .iPad2: return 9.7 case .iPad3: return 9.7 case .iPad4: return 9.7 @@ -936,6 +901,9 @@ public enum Device { case .appleWatchUltra3: return 2.2 case .appleWatchSeries11_42mm: return 1.9 case .appleWatchSeries11_46mm: return 2.0 + case .appleWatchSeries12_42mm: return 1.79 + case .appleWatchSeries12_46mm: return 1.99 + case .appleWatchUltra4: return 2.04 case .simulator(let model): return model.diagonal case .unknown: return -1 } @@ -999,6 +967,8 @@ public enum Device { case .iPhone17ProMax: return (width: 9, height: 19.5) case .iPhoneAir: return (width: 9, height: 19.5) case .iPhone17e: return (width: 9, height: 19.5) + case .iPhone18Pro: return (width: 9, height: 19.5) + case .iPhone18ProMax: return (width: 9, height: 19.5) case .iPad2: return (width: 3, height: 4) case .iPad3: return (width: 3, height: 4) case .iPad4: return (width: 3, height: 4) @@ -1082,6 +1052,9 @@ public enum Device { case .appleWatchUltra3: return (width: 4, height: 5) case .appleWatchSeries11_42mm: return (width: 374, height: 446) case .appleWatchSeries11_46mm: return (width: 416, height: 496) + case .appleWatchSeries12_42mm: return (width: 374, height: 446) + case .appleWatchSeries12_46mm: return (width: 416, height: 496) + case .appleWatchUltra4: return (width: 422, height: 514) case .simulator(let model): return model.screenRatio case .unknown: return (width: -1, height: -1) } @@ -1100,7 +1073,7 @@ public enum Device { /// All iPhones public static var allPhones: [Device] { - return [.iPhone4, .iPhone4s, .iPhone5, .iPhone5c, .iPhone5s, .iPhone6, .iPhone6Plus, .iPhone6s, .iPhone6sPlus, .iPhone7, .iPhone7Plus, .iPhoneSE, .iPhone8, .iPhone8Plus, .iPhoneX, .iPhoneXS, .iPhoneXSMax, .iPhoneXR, .iPhone11, .iPhone11Pro, .iPhone11ProMax, .iPhoneSE2, .iPhone12, .iPhone12Mini, .iPhone12Pro, .iPhone12ProMax, .iPhone13, .iPhone13Mini, .iPhone13Pro, .iPhone13ProMax, .iPhoneSE3, .iPhone14, .iPhone14Plus, .iPhone14Pro, .iPhone14ProMax, .iPhone15, .iPhone15Plus, .iPhone15Pro, .iPhone15ProMax, .iPhone16, .iPhone16Plus, .iPhone16Pro, .iPhone16ProMax, .iPhone16e, .iPhone17, .iPhone17Pro, .iPhone17ProMax, .iPhoneAir, .iPhone17e] + return [.iPhone4, .iPhone4s, .iPhone5, .iPhone5c, .iPhone5s, .iPhone6, .iPhone6Plus, .iPhone6s, .iPhone6sPlus, .iPhone7, .iPhone7Plus, .iPhoneSE, .iPhone8, .iPhone8Plus, .iPhoneX, .iPhoneXS, .iPhoneXSMax, .iPhoneXR, .iPhone11, .iPhone11Pro, .iPhone11ProMax, .iPhoneSE2, .iPhone12, .iPhone12Mini, .iPhone12Pro, .iPhone12ProMax, .iPhone13, .iPhone13Mini, .iPhone13Pro, .iPhone13ProMax, .iPhoneSE3, .iPhone14, .iPhone14Plus, .iPhone14Pro, .iPhone14ProMax, .iPhone15, .iPhone15Plus, .iPhone15Pro, .iPhone15ProMax, .iPhone16, .iPhone16Plus, .iPhone16Pro, .iPhone16ProMax, .iPhone16e, .iPhone17, .iPhone17Pro, .iPhone17ProMax, .iPhoneAir, .iPhone17e, .iPhone18Pro, .iPhone18ProMax] } /// All iPads @@ -1111,17 +1084,17 @@ public enum Device { /// All X-Series Devices @available(*, deprecated, renamed: "allDevicesWithSensorHousing") public static var allXSeriesDevices: [Device] { - return [.iPhoneX, .iPhoneXS, .iPhoneXSMax, .iPhoneXR, .iPhone11, .iPhone11Pro, .iPhone11ProMax, .iPhone12, .iPhone12Mini, .iPhone12Pro, .iPhone12ProMax, .iPhone13, .iPhone13Mini, .iPhone13Pro, .iPhone13ProMax, .iPhone14, .iPhone14Plus, .iPhone14Pro, .iPhone14ProMax, .iPhone15, .iPhone15Plus, .iPhone15Pro, .iPhone15ProMax, .iPhone16, .iPhone16Plus, .iPhone16Pro, .iPhone16ProMax, .iPhone16e, .iPhone17, .iPhone17Pro, .iPhone17ProMax, .iPhoneAir, .iPhone17e] + return [.iPhoneX, .iPhoneXS, .iPhoneXSMax, .iPhoneXR, .iPhone11, .iPhone11Pro, .iPhone11ProMax, .iPhone12, .iPhone12Mini, .iPhone12Pro, .iPhone12ProMax, .iPhone13, .iPhone13Mini, .iPhone13Pro, .iPhone13ProMax, .iPhone14, .iPhone14Plus, .iPhone14Pro, .iPhone14ProMax, .iPhone15, .iPhone15Plus, .iPhone15Pro, .iPhone15ProMax, .iPhone16, .iPhone16Plus, .iPhone16Pro, .iPhone16ProMax, .iPhone16e, .iPhone17, .iPhone17Pro, .iPhone17ProMax, .iPhoneAir, .iPhone17e, .iPhone18Pro, .iPhone18ProMax] } /// All Plus and Max-Sized Devices public static var allPlusSizedDevices: [Device] { - return [.iPhone6Plus, .iPhone6sPlus, .iPhone7Plus, .iPhone8Plus, .iPhoneXSMax, .iPhone11ProMax, .iPhone12ProMax, .iPhone13ProMax, .iPhone14Plus, .iPhone14ProMax, .iPhone15Plus, .iPhone15ProMax, .iPhone16Plus, .iPhone16ProMax, .iPhone17ProMax, .iPhoneAir] + return [.iPhone6Plus, .iPhone6sPlus, .iPhone7Plus, .iPhone8Plus, .iPhoneXSMax, .iPhone11ProMax, .iPhone12ProMax, .iPhone13ProMax, .iPhone14Plus, .iPhone14ProMax, .iPhone15Plus, .iPhone15ProMax, .iPhone16Plus, .iPhone16ProMax, .iPhone17ProMax, .iPhoneAir, .iPhone18ProMax] } /// All Pro Devices public static var allProDevices: [Device] { - return [.iPhone11Pro, .iPhone11ProMax, .iPhone12Pro, .iPhone12ProMax, .iPhone13Pro, .iPhone13ProMax, .iPhone14Pro, .iPhone14ProMax, .iPhone15Pro, .iPhone15ProMax, .iPhone16Pro, .iPhone16ProMax, .iPhone17Pro, .iPhone17ProMax, .iPadPro9Inch, .iPadPro12Inch, .iPadPro12Inch2, .iPadPro10Inch, .iPadPro11Inch, .iPadPro12Inch3, .iPadPro11Inch2, .iPadPro12Inch4, .iPadPro11Inch3, .iPadPro12Inch5, .iPadPro11Inch4, .iPadPro12Inch6, .iPadPro11M4, .iPadPro13M4, .iPadPro11M5, .iPadPro13M5] + return [.iPhone11Pro, .iPhone11ProMax, .iPhone12Pro, .iPhone12ProMax, .iPhone13Pro, .iPhone13ProMax, .iPhone14Pro, .iPhone14ProMax, .iPhone15Pro, .iPhone15ProMax, .iPhone16Pro, .iPhone16ProMax, .iPhone17Pro, .iPhone17ProMax, .iPhone18Pro, .iPhone18ProMax, .iPadPro9Inch, .iPadPro12Inch, .iPadPro12Inch2, .iPadPro10Inch, .iPadPro11Inch, .iPadPro12Inch3, .iPadPro11Inch2, .iPadPro12Inch4, .iPadPro11Inch3, .iPadPro12Inch5, .iPadPro11Inch4, .iPadPro12Inch6, .iPadPro11M4, .iPadPro13M4, .iPadPro11M5, .iPadPro13M5] } /// All mini Devices @@ -1207,12 +1180,12 @@ public enum Device { /// All Face ID Capable Devices public static var allFaceIDCapableDevices: [Device] { - return [.iPhoneX, .iPhoneXS, .iPhoneXSMax, .iPhoneXR, .iPhone11, .iPhone11Pro, .iPhone11ProMax, .iPhone12, .iPhone12Mini, .iPhone12Pro, .iPhone12ProMax, .iPhone13, .iPhone13Mini, .iPhone13Pro, .iPhone13ProMax, .iPhone14, .iPhone14Plus, .iPhone14Pro, .iPhone14ProMax, .iPhone15, .iPhone15Plus, .iPhone15Pro, .iPhone15ProMax, .iPhone16, .iPhone16Plus, .iPhone16Pro, .iPhone16ProMax, .iPhone16e, .iPhone17, .iPhone17Pro, .iPhone17ProMax, .iPhoneAir, .iPhone17e, .iPadPro11Inch, .iPadPro12Inch3, .iPadPro11Inch2, .iPadPro12Inch4, .iPadPro11Inch3, .iPadPro12Inch5, .iPadPro11Inch4, .iPadPro12Inch6, .iPadPro11M4, .iPadPro13M4, .iPadPro11M5, .iPadPro13M5] + return [.iPhoneX, .iPhoneXS, .iPhoneXSMax, .iPhoneXR, .iPhone11, .iPhone11Pro, .iPhone11ProMax, .iPhone12, .iPhone12Mini, .iPhone12Pro, .iPhone12ProMax, .iPhone13, .iPhone13Mini, .iPhone13Pro, .iPhone13ProMax, .iPhone14, .iPhone14Plus, .iPhone14Pro, .iPhone14ProMax, .iPhone15, .iPhone15Plus, .iPhone15Pro, .iPhone15ProMax, .iPhone16, .iPhone16Plus, .iPhone16Pro, .iPhone16ProMax, .iPhone16e, .iPhone17, .iPhone17Pro, .iPhone17ProMax, .iPhoneAir, .iPhone17e, .iPhone18Pro, .iPhone18ProMax, .iPadPro11Inch, .iPadPro12Inch3, .iPadPro11Inch2, .iPadPro12Inch4, .iPadPro11Inch3, .iPadPro12Inch5, .iPadPro11Inch4, .iPadPro12Inch6, .iPadPro11M4, .iPadPro13M4, .iPadPro11M5, .iPadPro13M5] } /// All Devices with Touch ID or Face ID public static var allBiometricAuthenticationCapableDevices: [Device] { - return [.iPhone5s, .iPhone6, .iPhone6Plus, .iPhone6s, .iPhone6sPlus, .iPhone7, .iPhone7Plus, .iPhoneSE, .iPhone8, .iPhone8Plus, .iPhoneX, .iPhoneXS, .iPhoneXSMax, .iPhoneXR, .iPhone11, .iPhone11Pro, .iPhone11ProMax, .iPhoneSE2, .iPhone12, .iPhone12Mini, .iPhone12Pro, .iPhone12ProMax, .iPhone13, .iPhone13Mini, .iPhone13Pro, .iPhone13ProMax, .iPhoneSE3, .iPhone14, .iPhone14Plus, .iPhone14Pro, .iPhone14ProMax, .iPhone15, .iPhone15Plus, .iPhone15Pro, .iPhone15ProMax, .iPhone16, .iPhone16Plus, .iPhone16Pro, .iPhone16ProMax, .iPhone16e, .iPhone17, .iPhone17Pro, .iPhone17ProMax, .iPhoneAir, .iPhone17e, .iPadAir2, .iPad5, .iPad6, .iPadAir3, .iPad7, .iPad8, .iPad9, .iPad10, .iPadA16, .iPadAir4, .iPadAir5, .iPadAir11M2, .iPadAir13M2, .iPadAir11M3, .iPadAir13M3, .iPadAir11M4, .iPadAir13M4, .iPadMini3, .iPadMini4, .iPadMini5, .iPadMini6, .iPadMiniA17Pro, .iPadPro9Inch, .iPadPro12Inch, .iPadPro12Inch2, .iPadPro10Inch, .iPadPro11Inch, .iPadPro12Inch3, .iPadPro11Inch2, .iPadPro12Inch4, .iPadPro11Inch3, .iPadPro12Inch5, .iPadPro11Inch4, .iPadPro12Inch6, .iPadPro11M4, .iPadPro13M4, .iPadPro11M5, .iPadPro13M5] + return [.iPhone5s, .iPhone6, .iPhone6Plus, .iPhone6s, .iPhone6sPlus, .iPhone7, .iPhone7Plus, .iPhoneSE, .iPhone8, .iPhone8Plus, .iPhoneX, .iPhoneXS, .iPhoneXSMax, .iPhoneXR, .iPhone11, .iPhone11Pro, .iPhone11ProMax, .iPhoneSE2, .iPhone12, .iPhone12Mini, .iPhone12Pro, .iPhone12ProMax, .iPhone13, .iPhone13Mini, .iPhone13Pro, .iPhone13ProMax, .iPhoneSE3, .iPhone14, .iPhone14Plus, .iPhone14Pro, .iPhone14ProMax, .iPhone15, .iPhone15Plus, .iPhone15Pro, .iPhone15ProMax, .iPhone16, .iPhone16Plus, .iPhone16Pro, .iPhone16ProMax, .iPhone16e, .iPhone17, .iPhone17Pro, .iPhone17ProMax, .iPhoneAir, .iPhone17e, .iPhone18Pro, .iPhone18ProMax, .iPadAir2, .iPad5, .iPad6, .iPadAir3, .iPad7, .iPad8, .iPad9, .iPad10, .iPadA16, .iPadAir4, .iPadAir5, .iPadAir11M2, .iPadAir13M2, .iPadAir11M3, .iPadAir13M3, .iPadAir11M4, .iPadAir13M4, .iPadMini3, .iPadMini4, .iPadMini5, .iPadMini6, .iPadMiniA17Pro, .iPadPro9Inch, .iPadPro12Inch, .iPadPro12Inch2, .iPadPro10Inch, .iPadPro11Inch, .iPadPro12Inch3, .iPadPro11Inch2, .iPadPro12Inch4, .iPadPro11Inch3, .iPadPro12Inch5, .iPadPro11Inch4, .iPadPro12Inch6, .iPadPro11M4, .iPadPro13M4, .iPadPro11M5, .iPadPro13M5] } /// Returns whether or not the device has Touch ID @@ -1232,7 +1205,7 @@ public enum Device { /// All devices that feature a sensor housing in the screen public static var allDevicesWithSensorHousing: [Device] { - return [.iPhoneX, .iPhoneXS, .iPhoneXSMax, .iPhoneXR, .iPhone11, .iPhone11Pro, .iPhone11ProMax, .iPhone12, .iPhone12Mini, .iPhone12Pro, .iPhone12ProMax, .iPhone13, .iPhone13Mini, .iPhone13Pro, .iPhone13ProMax, .iPhone14, .iPhone14Plus, .iPhone14Pro, .iPhone14ProMax, .iPhone15, .iPhone15Plus, .iPhone15Pro, .iPhone15ProMax, .iPhone16, .iPhone16Plus, .iPhone16Pro, .iPhone16ProMax, .iPhone16e, .iPhone17, .iPhone17Pro, .iPhone17ProMax, .iPhoneAir, .iPhone17e] + return [.iPhoneX, .iPhoneXS, .iPhoneXSMax, .iPhoneXR, .iPhone11, .iPhone11Pro, .iPhone11ProMax, .iPhone12, .iPhone12Mini, .iPhone12Pro, .iPhone12ProMax, .iPhone13, .iPhone13Mini, .iPhone13Pro, .iPhone13ProMax, .iPhone14, .iPhone14Plus, .iPhone14Pro, .iPhone14ProMax, .iPhone15, .iPhone15Plus, .iPhone15Pro, .iPhone15ProMax, .iPhone16, .iPhone16Plus, .iPhone16Pro, .iPhone16ProMax, .iPhone16e, .iPhone17, .iPhone17Pro, .iPhone17ProMax, .iPhoneAir, .iPhone17e, .iPhone18Pro, .iPhone18ProMax] } /// All simulator devices that feature a sensor housing in the screen @@ -1247,7 +1220,7 @@ public enum Device { /// All devices that feature a screen with rounded corners. public static var allDevicesWithRoundedDisplayCorners: [Device] { - return [.iPhoneX, .iPhoneXS, .iPhoneXSMax, .iPhoneXR, .iPhone11, .iPhone11Pro, .iPhone11ProMax, .iPhone12, .iPhone12Mini, .iPhone12Pro, .iPhone12ProMax, .iPhone13, .iPhone13Mini, .iPhone13Pro, .iPhone13ProMax, .iPhone14, .iPhone14Plus, .iPhone14Pro, .iPhone14ProMax, .iPhone15, .iPhone15Plus, .iPhone15Pro, .iPhone15ProMax, .iPhone16, .iPhone16Plus, .iPhone16Pro, .iPhone16ProMax, .iPhone16e, .iPhone17, .iPhone17Pro, .iPhone17ProMax, .iPhoneAir, .iPhone17e, .iPad10, .iPadA16, .iPadAir4, .iPadAir5, .iPadAir11M2, .iPadAir13M2, .iPadAir11M3, .iPadAir13M3, .iPadAir11M4, .iPadAir13M4, .iPadMini6, .iPadMiniA17Pro, .iPadPro11Inch, .iPadPro12Inch3, .iPadPro11Inch2, .iPadPro12Inch4, .iPadPro11Inch3, .iPadPro12Inch5, .iPadPro11Inch4, .iPadPro12Inch6, .iPadPro11M4, .iPadPro13M4, .iPadPro11M5, .iPadPro13M5] + return [.iPhoneX, .iPhoneXS, .iPhoneXSMax, .iPhoneXR, .iPhone11, .iPhone11Pro, .iPhone11ProMax, .iPhone12, .iPhone12Mini, .iPhone12Pro, .iPhone12ProMax, .iPhone13, .iPhone13Mini, .iPhone13Pro, .iPhone13ProMax, .iPhone14, .iPhone14Plus, .iPhone14Pro, .iPhone14ProMax, .iPhone15, .iPhone15Plus, .iPhone15Pro, .iPhone15ProMax, .iPhone16, .iPhone16Plus, .iPhone16Pro, .iPhone16ProMax, .iPhone16e, .iPhone17, .iPhone17Pro, .iPhone17ProMax, .iPhoneAir, .iPhone17e, .iPhone18Pro, .iPhone18ProMax, .iPad10, .iPadA16, .iPadAir4, .iPadAir5, .iPadAir11M2, .iPadAir13M2, .iPadAir11M3, .iPadAir13M3, .iPadAir11M4, .iPadAir13M4, .iPadMini6, .iPadMiniA17Pro, .iPadPro11Inch, .iPadPro12Inch3, .iPadPro11Inch2, .iPadPro12Inch4, .iPadPro11Inch3, .iPadPro12Inch5, .iPadPro11Inch4, .iPadPro12Inch6, .iPadPro11M4, .iPadPro13M4, .iPadPro11M5, .iPadPro13M5] } /// Returns whether or not the device has a screen with rounded corners. @@ -1257,7 +1230,7 @@ public enum Device { /// All devices that have the Dynamic Island. public static var allDevicesWithDynamicIsland: [Device] { - return [.iPhone14Pro, .iPhone14ProMax, .iPhone15, .iPhone15Plus, .iPhone15Pro, .iPhone15ProMax, .iPhone16, .iPhone16Plus, .iPhone16Pro, .iPhone16ProMax, .iPhone17, .iPhone17Pro, .iPhone17ProMax, .iPhoneAir] + return [.iPhone14Pro, .iPhone14ProMax, .iPhone15, .iPhone15Plus, .iPhone15Pro, .iPhone15ProMax, .iPhone16, .iPhone16Plus, .iPhone16Pro, .iPhone16ProMax, .iPhone17, .iPhone17Pro, .iPhone17ProMax, .iPhoneAir, .iPhone18Pro, .iPhone18ProMax] } /// Returns whether or not the device has the Dynamic Island. @@ -1277,7 +1250,7 @@ public enum Device { /// All devices that support wireless charging. public static var allDevicesWithWirelessChargingSupport: [Device] { - return [.iPhone8, .iPhone8Plus, .iPhoneX, .iPhoneXS, .iPhoneXSMax, .iPhoneXR, .iPhone11, .iPhone11Pro, .iPhone11ProMax, .iPhoneSE2, .iPhone12, .iPhone12Mini, .iPhone12Pro, .iPhone12ProMax, .iPhone13, .iPhone13Mini, .iPhone13Pro, .iPhone13ProMax, .iPhoneSE3, .iPhone14, .iPhone14Plus, .iPhone14Pro, .iPhone14ProMax, .iPhone15, .iPhone15Plus, .iPhone15Pro, .iPhone15ProMax, .iPhone16, .iPhone16Plus, .iPhone16Pro, .iPhone16ProMax, .iPhone16e, .iPhone17, .iPhone17Pro, .iPhone17ProMax, .iPhoneAir, .iPhone17e] + return [.iPhone8, .iPhone8Plus, .iPhoneX, .iPhoneXS, .iPhoneXSMax, .iPhoneXR, .iPhone11, .iPhone11Pro, .iPhone11ProMax, .iPhoneSE2, .iPhone12, .iPhone12Mini, .iPhone12Pro, .iPhone12ProMax, .iPhone13, .iPhone13Mini, .iPhone13Pro, .iPhone13ProMax, .iPhoneSE3, .iPhone14, .iPhone14Plus, .iPhone14Pro, .iPhone14ProMax, .iPhone15, .iPhone15Plus, .iPhone15Pro, .iPhone15ProMax, .iPhone16, .iPhone16Plus, .iPhone16Pro, .iPhone16ProMax, .iPhone16e, .iPhone17, .iPhone17Pro, .iPhone17ProMax, .iPhoneAir, .iPhone17e, .iPhone18Pro, .iPhone18ProMax] } /// Returns whether or not the device supports wireless charging. @@ -1287,7 +1260,7 @@ public enum Device { /// All devices that support 5G. public static var allDevicesWith5gSupport: [Device] { - return [.iPhone12, .iPhone12Mini, .iPhone12Pro, .iPhone12ProMax, .iPhone13, .iPhone13Mini, .iPhone13Pro, .iPhone13ProMax, .iPhoneSE3, .iPhone14, .iPhone14Plus, .iPhone14Pro, .iPhone14ProMax, .iPhone15, .iPhone15Plus, .iPhone15Pro, .iPhone15ProMax, .iPhone16, .iPhone16Plus, .iPhone16Pro, .iPhone16ProMax, .iPhone16e, .iPhone17, .iPhone17Pro, .iPhone17ProMax, .iPhoneAir, .iPhone17e, .iPad10, .iPadA16, .iPadAir5, .iPadAir11M2, .iPadAir13M2, .iPadAir11M3, .iPadAir13M3, .iPadAir11M4, .iPadAir13M4, .iPadMini6, .iPadMiniA17Pro, .iPadPro11Inch3, .iPadPro12Inch5, .iPadPro11Inch4, .iPadPro12Inch6, .iPadPro11M4, .iPadPro13M4, .iPadPro11M5, .iPadPro13M5] + return [.iPhone12, .iPhone12Mini, .iPhone12Pro, .iPhone12ProMax, .iPhone13, .iPhone13Mini, .iPhone13Pro, .iPhone13ProMax, .iPhoneSE3, .iPhone14, .iPhone14Plus, .iPhone14Pro, .iPhone14ProMax, .iPhone15, .iPhone15Plus, .iPhone15Pro, .iPhone15ProMax, .iPhone16, .iPhone16Plus, .iPhone16Pro, .iPhone16ProMax, .iPhone16e, .iPhone17, .iPhone17Pro, .iPhone17ProMax, .iPhoneAir, .iPhone17e, .iPhone18Pro, .iPhone18ProMax, .iPad10, .iPadA16, .iPadAir5, .iPadAir11M2, .iPadAir13M2, .iPadAir11M3, .iPadAir13M3, .iPadAir11M4, .iPadAir13M4, .iPadMini6, .iPadMiniA17Pro, .iPadPro11Inch3, .iPadPro12Inch5, .iPadPro11Inch4, .iPadPro12Inch6, .iPadPro11M4, .iPadPro13M4, .iPadPro11M5, .iPadPro13M5] } /// Returns whether or not the device has 5G support. @@ -1302,7 +1275,7 @@ public enum Device { /// model capabilities and does not guarantee eSIM availability on a particular device. /// See https://support.apple.com/en-us/109317 and https://support.apple.com/en-us/119592. public static var allDevicesWithEsimSupport: [Device] { - return [.iPhoneXS, .iPhoneXSMax, .iPhoneXR, .iPhone11, .iPhone11Pro, .iPhone11ProMax, .iPhoneSE2, .iPhone12, .iPhone12Mini, .iPhone12Pro, .iPhone12ProMax, .iPhone13, .iPhone13Mini, .iPhone13Pro, .iPhone13ProMax, .iPhoneSE3, .iPhone14, .iPhone14Plus, .iPhone14Pro, .iPhone14ProMax, .iPhone15, .iPhone15Plus, .iPhone15Pro, .iPhone15ProMax, .iPhone16, .iPhone16Plus, .iPhone16Pro, .iPhone16ProMax, .iPhone16e, .iPhone17, .iPhone17Pro, .iPhone17ProMax, .iPhoneAir, .iPhone17e, .iPadAir3, .iPad7, .iPad8, .iPad9, .iPad10, .iPadA16, .iPadAir4, .iPadAir5, .iPadAir11M2, .iPadAir13M2, .iPadAir11M3, .iPadAir13M3, .iPadAir11M4, .iPadAir13M4, .iPadMini5, .iPadMini6, .iPadMiniA17Pro, .iPadPro11Inch, .iPadPro12Inch3, .iPadPro11Inch2, .iPadPro12Inch4, .iPadPro11Inch3, .iPadPro12Inch5, .iPadPro11Inch4, .iPadPro12Inch6, .iPadPro11M4, .iPadPro13M4, .iPadPro11M5, .iPadPro13M5] + return [.iPhoneXS, .iPhoneXSMax, .iPhoneXR, .iPhone11, .iPhone11Pro, .iPhone11ProMax, .iPhoneSE2, .iPhone12, .iPhone12Mini, .iPhone12Pro, .iPhone12ProMax, .iPhone13, .iPhone13Mini, .iPhone13Pro, .iPhone13ProMax, .iPhoneSE3, .iPhone14, .iPhone14Plus, .iPhone14Pro, .iPhone14ProMax, .iPhone15, .iPhone15Plus, .iPhone15Pro, .iPhone15ProMax, .iPhone16, .iPhone16Plus, .iPhone16Pro, .iPhone16ProMax, .iPhone16e, .iPhone17, .iPhone17Pro, .iPhone17ProMax, .iPhoneAir, .iPhone17e, .iPhone18Pro, .iPhone18ProMax, .iPadAir3, .iPad7, .iPad8, .iPad9, .iPad10, .iPadA16, .iPadAir4, .iPadAir5, .iPadAir11M2, .iPadAir13M2, .iPadAir11M3, .iPadAir13M3, .iPadAir11M4, .iPadAir13M4, .iPadMini5, .iPadMini6, .iPadMiniA17Pro, .iPadPro11Inch, .iPadPro12Inch3, .iPadPro11Inch2, .iPadPro12Inch4, .iPadPro11Inch3, .iPadPro12Inch5, .iPadPro11Inch4, .iPadPro12Inch6, .iPadPro11M4, .iPadPro13M4, .iPadPro11M5, .iPadPro13M5] } /// Returns whether the device model supports eSIM. Simulators use their simulated model. @@ -1315,7 +1288,7 @@ public enum Device { /// Support varies by country, region, and carrier; model membership does not guarantee /// eSIM availability on a particular device. These models also appear in `allDevicesWithEsimSupport`. public static var allDevicesWithDualEsimSupport: [Device] { - return [.iPhone13, .iPhone13Mini, .iPhone13Pro, .iPhone13ProMax, .iPhoneSE3, .iPhone14, .iPhone14Plus, .iPhone14Pro, .iPhone14ProMax, .iPhone15, .iPhone15Plus, .iPhone15Pro, .iPhone15ProMax, .iPhone16, .iPhone16Plus, .iPhone16Pro, .iPhone16ProMax, .iPhone16e, .iPhone17, .iPhone17Pro, .iPhone17ProMax, .iPhoneAir, .iPhone17e] + return [.iPhone13, .iPhone13Mini, .iPhone13Pro, .iPhone13ProMax, .iPhoneSE3, .iPhone14, .iPhone14Plus, .iPhone14Pro, .iPhone14ProMax, .iPhone15, .iPhone15Plus, .iPhone15Pro, .iPhone15ProMax, .iPhone16, .iPhone16Plus, .iPhone16Pro, .iPhone16ProMax, .iPhone16e, .iPhone17, .iPhone17Pro, .iPhone17ProMax, .iPhoneAir, .iPhone17e, .iPhone18Pro, .iPhone18ProMax] } /// Returns whether the device model supports two active eSIMs simultaneously. @@ -1326,7 +1299,7 @@ public enum Device { /// All devices that have a LiDAR sensor. public static var allDevicesWithALidarSensor: [Device] { - return [.iPhone12Pro, .iPhone12ProMax, .iPhone13Pro, .iPhone13ProMax, .iPhone14Pro, .iPhone14ProMax, .iPhone15Pro, .iPhone15ProMax, .iPhone16Pro, .iPhone16ProMax, .iPhone17Pro, .iPhone17ProMax, .iPadPro11Inch2, .iPadPro12Inch4, .iPadPro11Inch3, .iPadPro12Inch5, .iPadPro11Inch4, .iPadPro12Inch6, .iPadPro11M4, .iPadPro13M4, .iPadPro11M5, .iPadPro13M5] + return [.iPhone12Pro, .iPhone12ProMax, .iPhone13Pro, .iPhone13ProMax, .iPhone14Pro, .iPhone14ProMax, .iPhone15Pro, .iPhone15ProMax, .iPhone16Pro, .iPhone16ProMax, .iPhone17Pro, .iPhone17ProMax, .iPhone18Pro, .iPhone18ProMax, .iPadPro11Inch2, .iPadPro12Inch4, .iPadPro11Inch3, .iPadPro12Inch5, .iPadPro11Inch4, .iPadPro12Inch6, .iPadPro11M4, .iPadPro13M4, .iPadPro11M5, .iPadPro13M5] } /// Returns whether or not the device has a LiDAR sensor. @@ -1336,7 +1309,7 @@ public enum Device { /// All devices that have a USB-C connectivity. public static var allDevicesWithUSBCConnectivity: [Device] { - return [.iPhone15, .iPhone15Plus, .iPhone15Pro, .iPhone15ProMax, .iPhone16, .iPhone16Plus, .iPhone16Pro, .iPhone16ProMax, .iPhone16e, .iPhone17, .iPhone17Pro, .iPhone17ProMax, .iPhoneAir, .iPhone17e, .iPad10, .iPadA16, .iPadAir4, .iPadAir5, .iPadAir11M2, .iPadAir13M2, .iPadAir11M3, .iPadAir13M3, .iPadAir11M4, .iPadAir13M4, .iPadMini6, .iPadMiniA17Pro, .iPadPro11Inch, .iPadPro12Inch3, .iPadPro11Inch2, .iPadPro12Inch4, .iPadPro11Inch3, .iPadPro12Inch5, .iPadPro11Inch4, .iPadPro12Inch6, .iPadPro11M4, .iPadPro13M4, .iPadPro11M5, .iPadPro13M5] + return [.iPhone15, .iPhone15Plus, .iPhone15Pro, .iPhone15ProMax, .iPhone16, .iPhone16Plus, .iPhone16Pro, .iPhone16ProMax, .iPhone16e, .iPhone17, .iPhone17Pro, .iPhone17ProMax, .iPhoneAir, .iPhone17e, .iPhone18Pro, .iPhone18ProMax, .iPad10, .iPadA16, .iPadAir4, .iPadAir5, .iPadAir11M2, .iPadAir13M2, .iPadAir11M3, .iPadAir13M3, .iPadAir11M4, .iPadAir13M4, .iPadMini6, .iPadMiniA17Pro, .iPadPro11Inch, .iPadPro12Inch3, .iPadPro11Inch2, .iPadPro12Inch4, .iPadPro11Inch3, .iPadPro12Inch5, .iPadPro11Inch4, .iPadPro12Inch6, .iPadPro11M4, .iPadPro13M4, .iPadPro11M5, .iPadPro13M5] } /// Returns whether or not the device has a USB-C power supply. @@ -1356,7 +1329,7 @@ public enum Device { #elseif os(watchOS) /// All Watches public static var allWatches: [Device] { - return [.appleWatchSeries0_38mm, .appleWatchSeries0_42mm, .appleWatchSeries1_38mm, .appleWatchSeries1_42mm, .appleWatchSeries2_38mm, .appleWatchSeries2_42mm, .appleWatchSeries3_38mm, .appleWatchSeries3_42mm, .appleWatchSeries4_40mm, .appleWatchSeries4_44mm, .appleWatchSeries5_40mm, .appleWatchSeries5_44mm, .appleWatchSeries6_40mm, .appleWatchSeries6_44mm, .appleWatchSE_40mm, .appleWatchSE_44mm, .appleWatchSeries7_41mm, .appleWatchSeries7_45mm, .appleWatchSeries8_41mm, .appleWatchSeries8_45mm, .appleWatchSE2_40mm, .appleWatchSE2_44mm, .appleWatchUltra, .appleWatchSeries9_41mm, .appleWatchSeries9_45mm, .appleWatchUltra2, .appleWatchSE3_40mm, .appleWatchSE3_44mm, .appleWatchSeries10_42mm, .appleWatchSeries10_46mm, .appleWatchUltra3, .appleWatchSeries11_42mm, .appleWatchSeries11_46mm] + return [.appleWatchSeries0_38mm, .appleWatchSeries0_42mm, .appleWatchSeries1_38mm, .appleWatchSeries1_42mm, .appleWatchSeries2_38mm, .appleWatchSeries2_42mm, .appleWatchSeries3_38mm, .appleWatchSeries3_42mm, .appleWatchSeries4_40mm, .appleWatchSeries4_44mm, .appleWatchSeries5_40mm, .appleWatchSeries5_44mm, .appleWatchSeries6_40mm, .appleWatchSeries6_44mm, .appleWatchSE_40mm, .appleWatchSE_44mm, .appleWatchSeries7_41mm, .appleWatchSeries7_45mm, .appleWatchSeries8_41mm, .appleWatchSeries8_45mm, .appleWatchSE2_40mm, .appleWatchSE2_44mm, .appleWatchUltra, .appleWatchSeries9_41mm, .appleWatchSeries9_45mm, .appleWatchUltra2, .appleWatchSE3_40mm, .appleWatchSE3_44mm, .appleWatchSeries10_42mm, .appleWatchSeries10_46mm, .appleWatchUltra3, .appleWatchSeries11_42mm, .appleWatchSeries11_46mm, .appleWatchSeries12_42mm, .appleWatchSeries12_46mm, .appleWatchUltra4] } /// All simulator Watches @@ -1576,6 +1549,8 @@ public enum Device { case .iPhone17ProMax: return 460 case .iPhoneAir: return 460 case .iPhone17e: return 460 + case .iPhone18Pro: return 460 + case .iPhone18ProMax: return 460 case .iPad2: return 132 case .iPad3: return 264 case .iPad4: return 264 @@ -1659,6 +1634,9 @@ public enum Device { case .appleWatchUltra3: return 338 case .appleWatchSeries11_42mm: return 326 case .appleWatchSeries11_46mm: return 326 + case .appleWatchSeries12_42mm: return 326 + case .appleWatchSeries12_46mm: return 326 + case .appleWatchUltra4: return 326 case .simulator(let model): return model.ppi case .unknown: return nil } @@ -1754,6 +1732,8 @@ extension Device: CustomStringConvertible { case .iPhone17ProMax: return "iPhone 17 Pro Max" case .iPhoneAir: return "iPhone Air" case .iPhone17e: return "iPhone 17e" + case .iPhone18Pro: return "iPhone 18 Pro" + case .iPhone18ProMax: return "iPhone 18 Pro Max" case .iPad2: return "iPad 2" case .iPad3: return "iPad (3rd generation)" case .iPad4: return "iPad (4th generation)" @@ -1837,6 +1817,9 @@ extension Device: CustomStringConvertible { case .appleWatchUltra3: return "Apple Watch Ultra 3" case .appleWatchSeries11_42mm: return "Apple Watch Series 11 42mm" case .appleWatchSeries11_46mm: return "Apple Watch Series 11 46mm" + case .appleWatchSeries12_42mm: return "Apple Watch Series 12 42mm" + case .appleWatchSeries12_46mm: return "Apple Watch Series 12 46mm" + case .appleWatchUltra4: return "Apple Watch Ultra 4" case .simulator(let model): return "Simulator (\(model.description))" case .unknown(let identifier): return identifier } @@ -1919,6 +1902,8 @@ extension Device: CustomStringConvertible { case .iPhone17ProMax: return "iPhone 17 Pro Max" case .iPhoneAir: return "iPhone Air" case .iPhone17e: return "iPhone 17e" + case .iPhone18Pro: return "iPhone 18 Pro" + case .iPhone18ProMax: return "iPhone 18 Pro Max" case .iPad2: return "iPad 2" case .iPad3: return "iPad (3rd generation)" case .iPad4: return "iPad (4th generation)" @@ -2002,6 +1987,9 @@ extension Device: CustomStringConvertible { case .appleWatchUltra3: return "Apple Watch Ultra 3" case .appleWatchSeries11_42mm: return "Apple Watch Series 11 42mm" case .appleWatchSeries11_46mm: return "Apple Watch Series 11 46mm" + case .appleWatchSeries12_42mm: return "Apple Watch Series 12 42mm" + case .appleWatchSeries12_46mm: return "Apple Watch Series 12 46mm" + case .appleWatchUltra4: return "Apple Watch Ultra 4" case .simulator(let model): return "Simulator (\(model.safeDescription))" case .unknown(let identifier): return identifier } @@ -2441,13 +2429,15 @@ extension Device { case .iPhone16ProMax: return [.wide, .telephoto, .ultraWide] case .iPhone17Pro: return [.wide, .telephoto, .ultraWide] case .iPhone17ProMax: return [.wide, .telephoto, .ultraWide] + case .iPhone18Pro: return [.wide, .telephoto, .ultraWide] + case .iPhone18ProMax: return [.wide, .telephoto, .ultraWide] default: return [] } } /// All devices that feature a camera public static var allDevicesWithCamera: [Device] { - return [.iPodTouch5, .iPodTouch6, .iPodTouch7, .iPhone4, .iPhone4s, .iPhone5, .iPhone5c, .iPhone5s, .iPhone6, .iPhone6Plus, .iPhone6s, .iPhone6sPlus, .iPhone7, .iPhone7Plus, .iPhoneSE, .iPhone8, .iPhone8Plus, .iPhoneX, .iPhoneXS, .iPhoneXSMax, .iPhoneXR, .iPhone11, .iPhone11Pro, .iPhone11ProMax, .iPhoneSE2, .iPhone12, .iPhone12Mini, .iPhone12Pro, .iPhone12ProMax, .iPhone13, .iPhone13Mini, .iPhone13Pro, .iPhone13ProMax, .iPhoneSE3, .iPhone14, .iPhone14Plus, .iPhone14Pro, .iPhone14ProMax, .iPhone15, .iPhone15Plus, .iPhone15Pro, .iPhone15ProMax, .iPhone16, .iPhone16Plus, .iPhone16Pro, .iPhone16ProMax, .iPhone16e, .iPhone17, .iPhone17Pro, .iPhone17ProMax, .iPhoneAir, .iPhone17e, .iPad2, .iPad3, .iPad4, .iPadAir, .iPadAir2, .iPad5, .iPad6, .iPadAir3, .iPad7, .iPad8, .iPad9, .iPad10, .iPadA16, .iPadAir4, .iPadAir5, .iPadAir11M2, .iPadAir13M2, .iPadAir11M3, .iPadAir13M3, .iPadAir11M4, .iPadAir13M4, .iPadMini, .iPadMini2, .iPadMini3, .iPadMini4, .iPadMini5, .iPadMini6, .iPadMiniA17Pro, .iPadPro9Inch, .iPadPro12Inch, .iPadPro12Inch2, .iPadPro10Inch, .iPadPro11Inch, .iPadPro12Inch3, .iPadPro11Inch2, .iPadPro12Inch4, .iPadPro11Inch3, .iPadPro12Inch5, .iPadPro11Inch4, .iPadPro12Inch6, .iPadPro11M4, .iPadPro13M4, .iPadPro11M5, .iPadPro13M5] + return [.iPodTouch5, .iPodTouch6, .iPodTouch7, .iPhone4, .iPhone4s, .iPhone5, .iPhone5c, .iPhone5s, .iPhone6, .iPhone6Plus, .iPhone6s, .iPhone6sPlus, .iPhone7, .iPhone7Plus, .iPhoneSE, .iPhone8, .iPhone8Plus, .iPhoneX, .iPhoneXS, .iPhoneXSMax, .iPhoneXR, .iPhone11, .iPhone11Pro, .iPhone11ProMax, .iPhoneSE2, .iPhone12, .iPhone12Mini, .iPhone12Pro, .iPhone12ProMax, .iPhone13, .iPhone13Mini, .iPhone13Pro, .iPhone13ProMax, .iPhoneSE3, .iPhone14, .iPhone14Plus, .iPhone14Pro, .iPhone14ProMax, .iPhone15, .iPhone15Plus, .iPhone15Pro, .iPhone15ProMax, .iPhone16, .iPhone16Plus, .iPhone16Pro, .iPhone16ProMax, .iPhone16e, .iPhone17, .iPhone17Pro, .iPhone17ProMax, .iPhoneAir, .iPhone17e, .iPhone18Pro, .iPhone18ProMax, .iPad2, .iPad3, .iPad4, .iPadAir, .iPadAir2, .iPad5, .iPad6, .iPadAir3, .iPad7, .iPad8, .iPad9, .iPad10, .iPadA16, .iPadAir4, .iPadAir5, .iPadAir11M2, .iPadAir13M2, .iPadAir11M3, .iPadAir13M3, .iPadAir11M4, .iPadAir13M4, .iPadMini, .iPadMini2, .iPadMini3, .iPadMini4, .iPadMini5, .iPadMini6, .iPadMiniA17Pro, .iPadPro9Inch, .iPadPro12Inch, .iPadPro12Inch2, .iPadPro10Inch, .iPadPro11Inch, .iPadPro12Inch3, .iPadPro11Inch2, .iPadPro12Inch4, .iPadPro11Inch3, .iPadPro12Inch5, .iPadPro11Inch4, .iPadPro12Inch6, .iPadPro11M4, .iPadPro13M4, .iPadPro11M5, .iPadPro13M5] } /// All devices that feature a normal camera @@ -2458,17 +2448,17 @@ extension Device { /// All devices that feature a wide camera public static var allDevicesWithWideCamera: [Device] { - return [.iPodTouch5, .iPodTouch6, .iPodTouch7, .iPhone4, .iPhone4s, .iPhone5, .iPhone5c, .iPhone5s, .iPhone6, .iPhone6Plus, .iPhone6s, .iPhone6sPlus, .iPhone7, .iPhone7Plus, .iPhoneSE, .iPhone8, .iPhone8Plus, .iPhoneX, .iPhoneXS, .iPhoneXSMax, .iPhoneXR, .iPhone11, .iPhone11Pro, .iPhone11ProMax, .iPhoneSE2, .iPhone12, .iPhone12Mini, .iPhone12Pro, .iPhone12ProMax, .iPhone13, .iPhone13Mini, .iPhone13Pro, .iPhone13ProMax, .iPhoneSE3, .iPhone14, .iPhone14Plus, .iPhone14Pro, .iPhone14ProMax, .iPhone15, .iPhone15Plus, .iPhone15Pro, .iPhone15ProMax, .iPhone16, .iPhone16Plus, .iPhone16Pro, .iPhone16ProMax, .iPhone16e, .iPhone17, .iPhone17Pro, .iPhone17ProMax, .iPhoneAir, .iPhone17e, .iPad2, .iPad3, .iPad4, .iPadAir, .iPadAir2, .iPad5, .iPad6, .iPadAir3, .iPad7, .iPad8, .iPad9, .iPad10, .iPadA16, .iPadAir4, .iPadAir5, .iPadAir11M2, .iPadAir13M2, .iPadAir11M3, .iPadAir13M3, .iPadAir11M4, .iPadAir13M4, .iPadMini, .iPadMini2, .iPadMini3, .iPadMini4, .iPadMini5, .iPadMini6, .iPadMiniA17Pro, .iPadPro9Inch, .iPadPro12Inch, .iPadPro12Inch2, .iPadPro10Inch, .iPadPro11Inch, .iPadPro12Inch3, .iPadPro11Inch2, .iPadPro12Inch4, .iPadPro11Inch3, .iPadPro12Inch5, .iPadPro11Inch4, .iPadPro12Inch6, .iPadPro11M4, .iPadPro13M4, .iPadPro11M5, .iPadPro13M5] + return [.iPodTouch5, .iPodTouch6, .iPodTouch7, .iPhone4, .iPhone4s, .iPhone5, .iPhone5c, .iPhone5s, .iPhone6, .iPhone6Plus, .iPhone6s, .iPhone6sPlus, .iPhone7, .iPhone7Plus, .iPhoneSE, .iPhone8, .iPhone8Plus, .iPhoneX, .iPhoneXS, .iPhoneXSMax, .iPhoneXR, .iPhone11, .iPhone11Pro, .iPhone11ProMax, .iPhoneSE2, .iPhone12, .iPhone12Mini, .iPhone12Pro, .iPhone12ProMax, .iPhone13, .iPhone13Mini, .iPhone13Pro, .iPhone13ProMax, .iPhoneSE3, .iPhone14, .iPhone14Plus, .iPhone14Pro, .iPhone14ProMax, .iPhone15, .iPhone15Plus, .iPhone15Pro, .iPhone15ProMax, .iPhone16, .iPhone16Plus, .iPhone16Pro, .iPhone16ProMax, .iPhone16e, .iPhone17, .iPhone17Pro, .iPhone17ProMax, .iPhoneAir, .iPhone17e, .iPhone18Pro, .iPhone18ProMax, .iPad2, .iPad3, .iPad4, .iPadAir, .iPadAir2, .iPad5, .iPad6, .iPadAir3, .iPad7, .iPad8, .iPad9, .iPad10, .iPadA16, .iPadAir4, .iPadAir5, .iPadAir11M2, .iPadAir13M2, .iPadAir11M3, .iPadAir13M3, .iPadAir11M4, .iPadAir13M4, .iPadMini, .iPadMini2, .iPadMini3, .iPadMini4, .iPadMini5, .iPadMini6, .iPadMiniA17Pro, .iPadPro9Inch, .iPadPro12Inch, .iPadPro12Inch2, .iPadPro10Inch, .iPadPro11Inch, .iPadPro12Inch3, .iPadPro11Inch2, .iPadPro12Inch4, .iPadPro11Inch3, .iPadPro12Inch5, .iPadPro11Inch4, .iPadPro12Inch6, .iPadPro11M4, .iPadPro13M4, .iPadPro11M5, .iPadPro13M5] } /// All devices that feature a telephoto camera public static var allDevicesWithTelephotoCamera: [Device] { - return [.iPhone7Plus, .iPhone8Plus, .iPhoneX, .iPhoneXS, .iPhoneXSMax, .iPhone11Pro, .iPhone11ProMax, .iPhone12Pro, .iPhone12ProMax, .iPhone13Pro, .iPhone13ProMax, .iPhone14Pro, .iPhone14ProMax, .iPhone15Pro, .iPhone15ProMax, .iPhone16Pro, .iPhone16ProMax, .iPhone17Pro, .iPhone17ProMax] + return [.iPhone7Plus, .iPhone8Plus, .iPhoneX, .iPhoneXS, .iPhoneXSMax, .iPhone11Pro, .iPhone11ProMax, .iPhone12Pro, .iPhone12ProMax, .iPhone13Pro, .iPhone13ProMax, .iPhone14Pro, .iPhone14ProMax, .iPhone15Pro, .iPhone15ProMax, .iPhone16Pro, .iPhone16ProMax, .iPhone17Pro, .iPhone17ProMax, .iPhone18Pro, .iPhone18ProMax] } /// All devices that feature an ultra wide camera public static var allDevicesWithUltraWideCamera: [Device] { - return [.iPhone11, .iPhone11Pro, .iPhone11ProMax, .iPhone12, .iPhone12Mini, .iPhone12Pro, .iPhone12ProMax, .iPhone13, .iPhone13Mini, .iPhone13Pro, .iPhone13ProMax, .iPhone14, .iPhone14Plus, .iPhone14Pro, .iPhone14ProMax, .iPhone15, .iPhone15Plus, .iPhone15Pro, .iPhone15ProMax, .iPhone16, .iPhone16Plus, .iPhone16Pro, .iPhone16ProMax, .iPhone17, .iPhone17Pro, .iPhone17ProMax, .iPadPro11Inch2, .iPadPro12Inch4, .iPadPro11Inch3, .iPadPro12Inch5, .iPadPro11Inch4, .iPadPro12Inch6] + return [.iPhone11, .iPhone11Pro, .iPhone11ProMax, .iPhone12, .iPhone12Mini, .iPhone12Pro, .iPhone12ProMax, .iPhone13, .iPhone13Mini, .iPhone13Pro, .iPhone13ProMax, .iPhone14, .iPhone14Plus, .iPhone14Pro, .iPhone14ProMax, .iPhone15, .iPhone15Plus, .iPhone15Pro, .iPhone15ProMax, .iPhone16, .iPhone16Plus, .iPhone16Pro, .iPhone16ProMax, .iPhone17, .iPhone17Pro, .iPhone17ProMax, .iPhone18Pro, .iPhone18ProMax, .iPadPro11Inch2, .iPadPro12Inch4, .iPadPro11Inch3, .iPadPro12Inch5, .iPadPro11Inch4, .iPadPro12Inch6] } /// Returns whether or not the current device has a camera @@ -2563,6 +2553,7 @@ extension Device { case a18Pro case a19 case a19Pro + case a20Pro case m1 case m2 case m3 @@ -2580,6 +2571,7 @@ extension Device { case s8 case s9 case s10 + case s11 #endif case unknown } @@ -2640,6 +2632,8 @@ extension Device { case .iPhone17ProMax: return .a19Pro case .iPhoneAir: return .a19Pro case .iPhone17e: return .a19 + case .iPhone18Pro: return .a20Pro + case .iPhone18ProMax: return .a20Pro case .iPad2: return .a5 case .iPad3: return .a5X case .iPad4: return .a6X @@ -2723,6 +2717,9 @@ extension Device { case .appleWatchUltra3: return .s10 case .appleWatchSeries11_42mm: return .s10 case .appleWatchSeries11_46mm: return .s10 + case .appleWatchSeries12_42mm: return .s11 + case .appleWatchSeries12_46mm: return .s11 + case .appleWatchUltra4: return .s11 case .simulator(let model): return model.cpu case .unknown: return .unknown } @@ -2775,6 +2772,7 @@ extension Device.CPU: CustomStringConvertible { case .a18Pro: return "A18 Pro" case .a19: return "A19" case .a19Pro: return "A19 Pro" + case .a20Pro: return "A20 Pro" case .m1: return "M1" case .m2: return "M2" case .m3: return "M3" @@ -2795,6 +2793,7 @@ extension Device.CPU: CustomStringConvertible { case .s8: return "S8" case .s9: return "S9" case .s10: return "S10" + case .s11: return "S11" case .unknown: return "unknown" } #elseif os(visionOS) diff --git a/Source/Device.swift.gyb b/Source/Device.swift.gyb index a33cb126..63b4348a 100644 --- a/Source/Device.swift.gyb +++ b/Source/Device.swift.gyb @@ -270,6 +270,20 @@ iPhones = [ supportsWirelessCharging=True, hasRoundedDisplayCorners=True, cameras=1, hasUSBCConnectivity=True, has5gSupport=True, hasEsimSupport=True, hasDualEsimSupport=True), + + # Identifiers: https://api.ipsw.me/v4/devices (verified 2026-09-21). + # iPhone19,3 and iPhone19,7 are regional variants of iPhone 18 Pro Max. + Device("iPhone18Pro", "Device is an [iPhone 18 Pro](https://support.apple.com/en-us/148590)", "", ["iPhone19,2"], 6.3, (9, 19.5), "iPhone 18 Pro", "iPhone 18 Pro", 460, + cpu="a20Pro", isPro=True, isXSeries=True, hasFaceID=True, + hasSensorHousing=True, supportsWirelessCharging=True, hasRoundedDisplayCorners=True, hasDynamicIsland=True, + cameras=123, hasLidarSensor=True, hasUSBCConnectivity=True, has5gSupport=True, + hasEsimSupport=True, hasDualEsimSupport=True), + Device("iPhone18ProMax", "Device is an [iPhone 18 Pro Max](https://support.apple.com/en-us/148591)", "", ["iPhone19,3", "iPhone19,7"], 6.9, (9, 19.5), "iPhone 18 Pro Max", "iPhone 18 Pro Max", 460, + cpu="a20Pro", isPlusFormFactor=True, isPro=True, isXSeries=True, + hasFaceID=True, hasSensorHousing=True, supportsWirelessCharging=True, hasRoundedDisplayCorners=True, + hasDynamicIsland=True, cameras=123, hasLidarSensor=True, hasUSBCConnectivity=True, + has5gSupport=True, + hasEsimSupport=True, hasDualEsimSupport=True), ] iPads = [ @@ -665,6 +679,14 @@ watches = [ ["Watch7,18", "Watch7,20"], 2.0, (416,496), "Apple Watch Series 11 46mm", "Apple Watch Series 11 46mm", 326, cpu="s10", supportsWirelessCharging=True), + # Identifiers: https://gist.github.com/adamawolf/3048717 (verified 2026-09-21). + # Rectangular display diagonals calculated from Apple's pixel dimensions / PPI, rounded to two decimals. + Device("appleWatchSeries12_42mm", "Device is an [Apple Watch Series 12 42mm](https://support.apple.com/en-us/148589)", "", ["Watch8,2", "Watch8,4"], 1.79, (374,446), "Apple Watch Series 12 42mm", "Apple Watch Series 12 42mm", 326, + cpu="s11", supportsWirelessCharging=True), + Device("appleWatchSeries12_46mm", "Device is an [Apple Watch Series 12 46mm](https://support.apple.com/en-us/148589)", "", ["Watch8,3", "Watch8,5"], 1.99, (416,496), "Apple Watch Series 12 46mm", "Apple Watch Series 12 46mm", 326, + cpu="s11", supportsWirelessCharging=True), + Device("appleWatchUltra4", "Device is an [Apple Watch Ultra 4](https://support.apple.com/en-us/148758)", "", ["Watch8,1"], 2.04, (422,514), "Apple Watch Ultra 4", "Apple Watch Ultra 4", 326, + cpu="s11", supportsWirelessCharging=True), ] iOSDevices = iPods + iPhones + iPads + homePods @@ -714,22 +736,28 @@ public enum Device { #if os(iOS) % for device in iOSDevices: /// ${device.comment} +% if device.imageURL: /// /// ![Image](${device.imageURL}) +% end case ${device.caseName} % end #elseif os(tvOS) % for device in tvOSDevices: /// ${device.comment} +% if device.imageURL: /// /// ![Image](${device.imageURL}) +% end case ${device.caseName} % end #elseif os(watchOS) % for device in watchOSDevices: /// ${device.comment} +% if device.imageURL: /// /// ![Image](${device.imageURL}) +% end case ${device.caseName} % end #endif @@ -1881,6 +1909,7 @@ iOS_cpus = [ CPU("a18Pro" , "A18 Pro"), CPU("a19" , "A19"), CPU("a19Pro" , "A19 Pro"), + CPU("a20Pro" , "A20 Pro"), CPU("m1", "M1"), CPU("m2", "M2"), CPU("m3", "M3"), @@ -1900,6 +1929,7 @@ watchOS_cpus = [ CPU("s8", "S8"), CPU("s9", "S9"), CPU("s10", "S10"), + CPU("s11", "S11"), ] }% diff --git a/Tests/Tests.swift b/Tests/Tests.swift index 37561bc8..ce99bbc6 100644 --- a/Tests/Tests.swift +++ b/Tests/Tests.swift @@ -16,6 +16,65 @@ class DeviceKitTests: XCTestCase { let device = Device.current + #if os(iOS) || os(watchOS) + func testSeptember2026Devices() { + struct ExpectedDevice { + let model: Device + let identifiers: [String] + let diagonal: Double + let ratio: (Double, Double) + let ppi: Int + } + #if os(iOS) + let models: [ExpectedDevice] = [ + ExpectedDevice(model: .iPhone18Pro, identifiers: ["iPhone19,2"], diagonal: 6.3, ratio: (9, 19.5), ppi: 460), + ExpectedDevice(model: .iPhone18ProMax, identifiers: ["iPhone19,3", "iPhone19,7"], diagonal: 6.9, ratio: (9, 19.5), ppi: 460) + ] + let chip = Device.CPU.a20Pro + XCTAssertEqual(chip.description, "A20 Pro") + // The deferred model and unverified identifiers must remain unknown. + for identifier in ["iPhone19,1", "iPhone19,4", "iPhone19,5", "iPhone19,6", "placeholder:iPhone18Pro", "placeholder:iPhone18ProMax"] { + XCTAssertEqual(Device.mapToDevice(identifier: identifier), .unknown(identifier)) + } + #else + let models: [ExpectedDevice] = [ + ExpectedDevice(model: .appleWatchSeries12_42mm, identifiers: ["Watch8,2", "Watch8,4"], diagonal: 1.79, ratio: (374, 446), ppi: 326), + ExpectedDevice(model: .appleWatchSeries12_46mm, identifiers: ["Watch8,3", "Watch8,5"], diagonal: 1.99, ratio: (416, 496), ppi: 326), + ExpectedDevice(model: .appleWatchUltra4, identifiers: ["Watch8,1"], diagonal: 2.04, ratio: (422, 514), ppi: 326) + ] + let chip = Device.CPU.s11 + XCTAssertEqual(chip.description, "S11") + for identifier in ["Watch8,6", "Watch8,7", "Watch8,8", "Watch8,9", "placeholder:appleWatchSeries12_42mm", "placeholder:appleWatchSeries12_46mm", "placeholder:appleWatchUltra4"] { + XCTAssertEqual(Device.mapToDevice(identifier: identifier), .unknown(identifier)) + } + #endif + for expected in models { + let model = expected.model + for identifier in expected.identifiers { + XCTAssertEqual(Device.mapToDevice(identifier: identifier), model, identifier) + } + #if os(iOS) + XCTAssertTrue(model.isPhone) + XCTAssertFalse(model.isPad) + #endif + XCTAssertTrue(Device.allRealDevices.contains(model)) + XCTAssertTrue(Device.allSimulators.contains(.simulator(model))) + for variant in [model, .simulator(model)] { + XCTAssertEqual(variant.cpu, chip) + XCTAssertEqual(variant.diagonal, expected.diagonal) + XCTAssertEqual(variant.screenRatio.width, expected.ratio.0) + XCTAssertEqual(variant.screenRatio.height, expected.ratio.1) + XCTAssertEqual(variant.ppi, expected.ppi) + #if os(iOS) + XCTAssertTrue(variant.supportsWirelessCharging) + XCTAssertTrue(variant.hasEsimSupport) + XCTAssertTrue(variant.hasDualEsimSupport) + #endif + } + } + } + #endif + func testDeviceSimulator() { #if os(macOS) XCTAssertFalse(device.isOneOf(Device.allSimulators)) @@ -475,6 +534,7 @@ class DeviceKitTests: XCTestCase { .iPhone16ProMax, .iPhone17ProMax, .iPhoneAir, + .iPhone18ProMax, ]) } @@ -494,6 +554,8 @@ class DeviceKitTests: XCTestCase { .iPhone16ProMax, .iPhone17Pro, .iPhone17ProMax, + .iPhone18Pro, + .iPhone18ProMax, .iPadPro9Inch, .iPadPro12Inch, .iPadPro12Inch2, @@ -544,6 +606,8 @@ class DeviceKitTests: XCTestCase { .iPhone17, .iPhone17Pro, .iPhone17ProMax, + .iPhone18Pro, + .iPhone18ProMax, .iPhoneAir, ] for device in Device.allRealDevices { @@ -578,6 +642,8 @@ class DeviceKitTests: XCTestCase { .iPhone17, .iPhone17Pro, .iPhone17ProMax, + .iPhone18Pro, + .iPhone18ProMax, .iPhoneAir, .iPhone17e, .iPad10, @@ -722,6 +788,8 @@ class DeviceKitTests: XCTestCase { .iPhone16ProMax, .iPhone17Pro, .iPhone17ProMax, + .iPhone18Pro, + .iPhone18ProMax, .iPadPro11Inch2, .iPadPro12Inch4, .iPadPro11Inch3, @@ -752,6 +820,8 @@ class DeviceKitTests: XCTestCase { .iPhone17, .iPhone17Pro, .iPhone17ProMax, + .iPhone18Pro, + .iPhone18ProMax, .iPhoneAir, .iPhone17e, .iPad10, From 338c9e7ce24cac9e4f99e6b315ad8e146c42209c Mon Sep 17 00:00:00 2001 From: Zandor Smith Date: Mon, 21 Sep 2026 11:15:00 +0200 Subject: [PATCH 2/2] Consolidate new device changelog entry and list case values --- CHANGELOG.md | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d99894f8..2be319a1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,11 +2,17 @@ ## Unreleased -### New devices +### New September 2026 devices -- Add iPhone 18 Pro, iPhone 18 Pro Max, Apple Watch Series 12 (42mm/46mm), and Apple Watch Ultra 4 support, including A20 Pro and S11 chips. Adapted from [#507](https://github.com/devicekit/DeviceKit/pull/507), excluding iPhone Duo. -- Add hardware identifier mappings, including both regional iPhone 18 Pro Max variants and GPS/cellular Series 12 variants. -- Add Apple technical specification links and verified display metrics; Watch display diagonals are calculated from the published pixel dimensions and PPI. +This version adds support for iPhone 18 Pro, iPhone 18 Pro Max, Apple Watch Series 12 and Apple Watch Ultra 4, including hardware identifiers, display metrics and technical specification links: ([#512](https://github.com/devicekit/DeviceKit/pull/512)) + +| Device | Case value | +| --- | --- | +| iPhone 18 Pro | `Device.iPhone18Pro` | +| iPhone 18 Pro Max | `Device.iPhone18ProMax` | +| Apple Watch Series 12 42mm | `Device.appleWatchSeries12_42mm` | +| Apple Watch Series 12 46mm | `Device.appleWatchSeries12_46mm` | +| Apple Watch Ultra 4 | `Device.appleWatchUltra4` | ### New features @@ -16,7 +22,6 @@ - Update Apple support links for iPhone and iPad models, correct the iPhone 14 Pro Max pixel density to 460 ppi, and correct the iPad Air 11-inch (M2) aspect ratio to 41:59. ([#497](https://github.com/devicekit/DeviceKit/pull/497)) - ### 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))