Skip to content
30 changes: 30 additions & 0 deletions Sources/DeviceModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ public enum DeviceModel: CaseIterable {
case iPhone17e
case iPhone17Pro, iPhone17ProMax
case iPhoneAir
case iPhone18Pro, iPhone18ProMax
case iPhoneDuo

case iPadFirstGen, iPadSecondGen, iPadThirdGen, iPadFourthGen, iPadFifthGen, iPadSixthGen, iPadSeventhGen, iPadEighthGen, iPadNinthGen, iPadTenthGen
case iPadA16
Expand All @@ -59,6 +61,7 @@ public enum DeviceModel: CaseIterable {

case iPadAir11InchM2, iPadAir13InchM2
case iPadAir11InchM3, iPadAir13InchM3
case iPadAir11InchM4, iPadAir13InchM4

case iPadMini, iPadMini2, iPadMini3, iPadMini4, iPadMini5, iPadMini6
case iPadMiniA17Pro
Expand All @@ -74,6 +77,7 @@ public enum DeviceModel: CaseIterable {
case iPadPro11Inch_FourthGen, iPadPro12_9Inch_SixthGen

case iPadPro11InchM4, iPadPro13InchM4
case iPadPro11InchM5, iPadPro13InchM5

case iPodTouchFirstGen, iPodTouchSecondGen, iPodTouchThirdGen,
iPodTouchFourthGen, iPodTouchFifthGen, iPodTouchSixthGen, iPodTouchSeventhGen
Expand All @@ -97,6 +101,8 @@ public enum DeviceModel: CaseIterable {
case series11
case ultra3
case se3
case series12
case ultra4
#endif

case unknown
Expand Down Expand Up @@ -202,6 +208,11 @@ extension DeviceModel {
case (18, 3): return .iPhone17
case (18, 4): return .iPhoneAir
case (18, 5): return .iPhone17e

case (19, 2): return .iPhone18Pro
case (19, 3): return .iPhone18ProMax // Qualcomm modem, sold in the US only?
case (19, 4): return .iPhoneDuo
case (19, 7): return .iPhone18ProMax // Apple C2 modem, sold internationally?

default: return .unknown
}
Expand Down Expand Up @@ -241,6 +252,8 @@ extension DeviceModel {
case (14, 10), (14, 11): return .iPadAir13InchM2
case (15, 3), (15, 4): return .iPadAir11InchM3
case (15, 5), (15, 6): return .iPadAir13InchM3
case (16, 8), (15, 9): return .iPadAir11InchM4
case (16, 10), (16, 11): return .iPadAir13InchM4

case (2, 5), (2, 6), (2, 7): return .iPadMini
case (4, 4), (4, 5), (4, 6): return .iPadMini2
Expand Down Expand Up @@ -270,6 +283,10 @@ extension DeviceModel {
return .iPadPro11InchM4
case (16, 5), (16, 6):
return .iPadPro13InchM4
case (17, 1), (17, 2):
return .iPadPro11InchM5
case (17, 3), (17, 4):
return .iPadPro13InchM5
default: return .unknown
}
}
Expand Down Expand Up @@ -328,6 +345,8 @@ extension DeviceModel {
case (7, 17), (7, 18), (7, 19), (7, 20): return .series11
case (7, 12): return .ultra3
case (7, 13), (7, 14), (7, 15), (7, 16): return .se3
case (8, 1): return .ultra4
case (8, 2), (8, 3), (8, 4), (8, 5): return .series12
default: return .unknown
}
}
Expand Down Expand Up @@ -370,10 +389,21 @@ extension DeviceModel {
return true
case .iPhoneAir, .iPhone17, .iPhone17Pro, .iPhone17ProMax:
return true
case .iPhone18Pro, .iPhone18ProMax:
return true
default:
return false
}
}

public var isFoldable: Bool {
switch self {
case .iPhoneDuo:
return true
default:
return false
}
}
}
#endif

36 changes: 36 additions & 0 deletions Sources/Identifier.swift
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,14 @@ extension Identifier: CustomStringConvertible {
case (18, 5):
return "iPhone 17e"

case (19, 2):
return "iPhone 18 Pro"
case (19, 3):
return "iPhone 18 Pro Max" // Qualcomm modem, sold in the US only?
case (19, 4):
return "iPhone Duo"
case (19, 7):
return "iPhone 18 Pro Max" // Apple C2 modem, sold internationally?

default:
return "unknown"
Expand Down Expand Up @@ -448,6 +456,24 @@ extension Identifier: CustomStringConvertible {
return "iPad Air M3 (13 inch, Wi-Fi)"
case (15, 7):
return "iPad Air M3 (13 inch, Wi-Fi+5G)"

case (16, 8):
return "iPad Air M4 (11 inch, Wi-Fi)"
case (16, 9):
return "iPad Air M4 (11 inch, Wi-Fi+5G)"
case (16, 10):
return "iPad Air M4 (13 inch, Wi-Fi)"
case (16, 11):
return "iPad Air M4 (13 inch, Wi-Fi+5G)"

case (17, 1):
return "iPad Pro M5 (11 inch, Wi-Fi)"
case (17, 2):
return "iPad Pro M5 (11 inch, Wi-Fi+5G)"
case (17, 3):
return "iPad Pro M5 (13 inch, Wi-Fi)"
case (17, 4):
return "iPad Pro M5 (13 inch, Wi-Fi+5G)"

default:
return "unknown"
Expand Down Expand Up @@ -570,6 +596,16 @@ extension Identifier: CustomStringConvertible {
return "Apple Watch Series 11, 42mm case (GPS + Cellular)"
case (7, 20):
return "Apple Watch Series 11, 46mm case (GPS + Cellular)"
case (8, 1):
return "Apple Watch Ultra 4"
case (8, 2):
return "Apple Watch Series 12, 42mm case (GPS)"
case (8, 3):
return "Apple Watch Series 12, 46mm case (GPS)"
case (8, 4):
return "Apple Watch Series 12, 42mm case (GPS + Cellular)"
case (8, 5):
return "Apple Watch Series 12, 46mm case (GPS + Cellular)"
default:
return "unknown"
}
Expand Down
8 changes: 5 additions & 3 deletions Sources/Screen.swift
Original file line number Diff line number Diff line change
Expand Up @@ -139,10 +139,12 @@ extension Screen {
case (6, 7), (6, 9), (6, 15), (6, 17),
(7, 2), (7, 4): return .medium(mm: 45)

case (6, 18), (7, 5), (7, 12): return .ultra(mm: 49)
case (6, 18), (7, 5), (7, 12), (8, 1): return .ultra(mm: 49)

case (7, 8), (7, 10), (7, 17), (7, 19): return .small(mm: 42)
case (7, 9), (7, 11), (7, 18), (7, 20): return .medium(mm: 46)
case (7, 8), (7, 10), (7, 17), (7, 19),
(8, 2), (8, 4): return .small(mm: 42)
case (7, 9), (7, 11), (7, 18), (7, 20),
(8, 3), (8, 5): return .medium(mm: 46)

default: return nil
}
Expand Down
5 changes: 3 additions & 2 deletions Tests/DeviceModelTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -551,8 +551,9 @@ class DeviceModelTests: XCTestCase {
let withModels: [DeviceModel] = [.iPhone14Pro, .iPhone14ProMax,
.iPhone15, .iPhone15Plus, .iPhone15Pro, .iPhone15ProMax,
.iPhone16, .iPhone16Plus, .iPhone16Pro, .iPhone16ProMax,
.iPhone17, .iPhoneAir, .iPhone17Pro, .iPhone17ProMax]

.iPhone17, .iPhoneAir, .iPhone17Pro, .iPhone17ProMax,
.iPhone18Pro, .iPhone18ProMax]

let withoutModels: [DeviceModel] = DeviceModel.allCases.filter( { !withModels.contains($0) })

withModels.forEach { XCTAssertTrue($0.hasDynamicIsland) }
Expand Down
Loading