Autoría | Ultima modificación | Ver Log |
//===----------------------------------------------------------------------===////// This source file is part of the DeviceKit open source project//// Copyright © 2014 - 2018 Dennis Weissmann and the DeviceKit project authors//// License: https://github.com/dennisweissmann/DeviceKit/blob/master/LICENSE// Contributors: https://github.com/dennisweissmann/DeviceKit#contributors////===----------------------------------------------------------------------===//#if os(watchOS)import WatchKit#elseimport UIKit#endif// MARK: Device/// This enum is a value-type wrapper and extension of/// [`UIDevice`](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIDevice_Class/).////// Usage:////// let device = Device.current////// print(device) // prints, for example, "iPhone 6 Plus"////// if device == .iPhone6Plus {/// // Do something/// } else {/// // Do something else/// }////// ...////// if device.batteryState == .full || device.batteryState >= .charging(75) {/// print("Your battery is happy! 😊")/// }////// ...////// if device.batteryLevel >= 50 {/// install_iOS()/// } else {/// showError()/// }///public enum Device {#if os(iOS)/// Device is an [iPod touch (5th generation)](https://support.apple.com/kb/SP657)////// case iPodTouch5/// Device is an [iPod touch (6th generation)](https://support.apple.com/kb/SP720)////// case iPodTouch6/// Device is an [iPod touch (7th generation)](https://support.apple.com/kb/SP796)////// case iPodTouch7/// Device is an [iPhone 4](https://support.apple.com/kb/SP587)////// case iPhone4/// Device is an [iPhone 4s](https://support.apple.com/kb/SP643)////// case iPhone4s/// Device is an [iPhone 5](https://support.apple.com/kb/SP655)////// case iPhone5/// Device is an [iPhone 5c](https://support.apple.com/kb/SP684)////// case iPhone5c/// Device is an [iPhone 5s](https://support.apple.com/kb/SP685)////// case iPhone5s/// Device is an [iPhone 6](https://support.apple.com/kb/SP705)////// case iPhone6/// Device is an [iPhone 6 Plus](https://support.apple.com/kb/SP706)////// case iPhone6Plus/// Device is an [iPhone 6s](https://support.apple.com/kb/SP726)////// case iPhone6s/// Device is an [iPhone 6s Plus](https://support.apple.com/kb/SP727)////// case iPhone6sPlus/// Device is an [iPhone 7](https://support.apple.com/kb/SP743)////// case iPhone7/// Device is an [iPhone 7 Plus](https://support.apple.com/kb/SP744)////// case iPhone7Plus/// Device is an [iPhone SE](https://support.apple.com/kb/SP738)////// case iPhoneSE/// Device is an [iPhone 8](https://support.apple.com/kb/SP767)////// case iPhone8/// Device is an [iPhone 8 Plus](https://support.apple.com/kb/SP768)////// case iPhone8Plus/// Device is an [iPhone X](https://support.apple.com/kb/SP770)////// case iPhoneX/// Device is an [iPhone Xs](https://support.apple.com/kb/SP779)////// case iPhoneXS/// Device is an [iPhone Xs Max](https://support.apple.com/kb/SP780)////// case iPhoneXSMax/// Device is an [iPhone XÊ€](https://support.apple.com/kb/SP781)////// case iPhoneXR/// Device is an [iPhone 11](https://support.apple.com/kb/SP804)////// case iPhone11/// Device is an [iPhone 11 Pro](https://support.apple.com/kb/SP805)////// case iPhone11Pro/// Device is an [iPhone 11 Pro Max](https://support.apple.com/kb/SP806)////// case iPhone11ProMax/// Device is an [iPhone SE (2nd generation)](https://support.apple.com/kb/SP820)////// case iPhoneSE2/// Device is an [iPhone 12](https://support.apple.com/kb/SP830)////// case iPhone12/// Device is an [iPhone 12 mini](https://support.apple.com/kb/SP829)////// case iPhone12Mini/// Device is an [iPhone 12 Pro](https://support.apple.com/kb/SP831)////// case iPhone12Pro/// Device is an [iPhone 12 Pro Max](https://support.apple.com/kb/SP832)////// case iPhone12ProMax/// Device is an [iPhone 13](https://support.apple.com/kb/SP851)////// case iPhone13/// Device is an [iPhone 13 mini](https://support.apple.com/kb/SP847)////// case iPhone13Mini/// Device is an [iPhone 13 Pro](https://support.apple.com/kb/SP852)////// case iPhone13Pro/// Device is an [iPhone 13 Pro Max](https://support.apple.com/kb/SP848)////// case iPhone13ProMax/// Device is an [iPhone SE (3rd generation)](https://support.apple.com/kb/SP867)////// case iPhoneSE3/// Device is an [iPad 2](https://support.apple.com/kb/SP622)////// case iPad2/// Device is an [iPad (3rd generation)](https://support.apple.com/kb/SP647)////// case iPad3/// Device is an [iPad (4th generation)](https://support.apple.com/kb/SP662)////// case iPad4/// Device is an [iPad Air](https://support.apple.com/kb/SP692)////// case iPadAir/// Device is an [iPad Air 2](https://support.apple.com/kb/SP708)////// case iPadAir2/// Device is an [iPad (5th generation)](https://support.apple.com/kb/SP751)////// case iPad5/// Device is an [iPad (6th generation)](https://support.apple.com/kb/SP774)////// case iPad6/// Device is an [iPad Air (3rd generation)](https://support.apple.com/kb/SP787)////// case iPadAir3/// Device is an [iPad (7th generation)](https://support.apple.com/kb/SP807)////// case iPad7/// Device is an [iPad (8th generation)](https://support.apple.com/kb/SP822)////// case iPad8/// Device is an [iPad (9th generation)](https://support.apple.com/kb/SP849)////// case iPad9/// Device is an [iPad Air (4th generation)](https://support.apple.com/kb/SP828)////// case iPadAir4/// Device is an [iPad Air (5th generation)](https://support.apple.com/kb/TODO)////// case iPadAir5/// Device is an [iPad Mini](https://support.apple.com/kb/SP661)////// case iPadMini/// Device is an [iPad Mini 2](https://support.apple.com/kb/SP693)////// case iPadMini2/// Device is an [iPad Mini 3](https://support.apple.com/kb/SP709)////// case iPadMini3/// Device is an [iPad Mini 4](https://support.apple.com/kb/SP725)////// case iPadMini4/// Device is an [iPad Mini (5th generation)](https://support.apple.com/kb/SP788)////// case iPadMini5/// Device is an [iPad Mini (6th generation)](https://support.apple.com/kb/SP850)////// case iPadMini6/// Device is an [iPad Pro 9.7-inch](https://support.apple.com/kb/SP739)////// case iPadPro9Inch/// Device is an [iPad Pro 12-inch](https://support.apple.com/kb/SP723)////// case iPadPro12Inch/// Device is an [iPad Pro 12-inch (2nd generation)](https://support.apple.com/kb/SP761)////// case iPadPro12Inch2/// Device is an [iPad Pro 10.5-inch](https://support.apple.com/kb/SP762)////// case iPadPro10Inch/// Device is an [iPad Pro 11-inch](https://support.apple.com/kb/SP784)////// case iPadPro11Inch/// Device is an [iPad Pro 12.9-inch (3rd generation)](https://support.apple.com/kb/SP785)////// case iPadPro12Inch3/// Device is an [iPad Pro 11-inch (2nd generation)](https://support.apple.com/kb/SP814)////// case iPadPro11Inch2/// Device is an [iPad Pro 12.9-inch (4th generation)](https://support.apple.com/kb/SP815)////// case iPadPro12Inch4/// Device is an [iPad Pro 11-inch (3rd generation)](https://support.apple.com/kb/SP843)////// case iPadPro11Inch3/// Device is an [iPad Pro 12.9-inch (5th generation)](https://support.apple.com/kb/SP844)////// case iPadPro12Inch5/// Device is a [HomePod](https://support.apple.com/kb/SP773)////// case homePod#elseif os(tvOS)/// Device is an [Apple TV HD](https://support.apple.com/kb/SP724) (Previously Apple TV (4th generation))////// case appleTVHD/// Device is an [Apple TV 4K](https://support.apple.com/kb/SP769)////// case appleTV4K/// Device is an [Apple TV 4K (2nd generation)](https://support.apple.com/kb/SP845)////// case appleTV4K2#elseif os(watchOS)/// Device is an [Apple Watch (1st generation)](https://support.apple.com/kb/SP735)////// case appleWatchSeries0_38mm/// Device is an [Apple Watch (1st generation)](https://support.apple.com/kb/SP735)////// case appleWatchSeries0_42mm/// Device is an [Apple Watch Series 1](https://support.apple.com/kb/SP745)////// case appleWatchSeries1_38mm/// Device is an [Apple Watch Series 1](https://support.apple.com/kb/SP745)////// case appleWatchSeries1_42mm/// Device is an [Apple Watch Series 2](https://support.apple.com/kb/SP746)////// case appleWatchSeries2_38mm/// Device is an [Apple Watch Series 2](https://support.apple.com/kb/SP746)////// case appleWatchSeries2_42mm/// Device is an [Apple Watch Series 3](https://support.apple.com/kb/SP766)////// case appleWatchSeries3_38mm/// Device is an [Apple Watch Series 3](https://support.apple.com/kb/SP766)////// case appleWatchSeries3_42mm/// Device is an [Apple Watch Series 4](https://support.apple.com/kb/SP778)////// case appleWatchSeries4_40mm/// Device is an [Apple Watch Series 4](https://support.apple.com/kb/SP778)////// case appleWatchSeries4_44mm/// Device is an [Apple Watch Series 5](https://support.apple.com/kb/SP808)////// case appleWatchSeries5_40mm/// Device is an [Apple Watch Series 5](https://support.apple.com/kb/SP808)////// case appleWatchSeries5_44mm/// Device is an [Apple Watch Series 6](https://support.apple.com/kb/SP826)////// case appleWatchSeries6_40mm/// Device is an [Apple Watch Series 6](https://support.apple.com/kb/SP826)////// case appleWatchSeries6_44mm/// Device is an [Apple Watch SE](https://support.apple.com/kb/SP827)////// case appleWatchSE_40mm/// Device is an [Apple Watch SE](https://support.apple.com/kb/SP827)////// case appleWatchSE_44mm#endif/// Device is [Simulator](https://developer.apple.com/library/ios/documentation/IDEs/Conceptual/iOS_Simulator_Guide/Introduction/Introduction.html)////// indirect case simulator(Device)/// Device is not yet known (implemented)/// You can still use this enum as before but the description equals the identifier (you can get multiple identifiers for the same product class/// (e.g. "iPhone6,1" or "iPhone 6,2" do both mean "iPhone 5s"))case unknown(String)/// Returns a `Device` representing the current device this software runs on.public static var current: Device {return Device.mapToDevice(identifier: Device.identifier)}/// Gets the identifier from the system, such as "iPhone7,1".public static var identifier: String = {var systemInfo = utsname()uname(&systemInfo)let mirror = Mirror(reflecting: systemInfo.machine)let identifier = mirror.children.reduce("") { identifier, element inguard let value = element.value as? Int8, value != 0 else { return identifier }return identifier + String(UnicodeScalar(UInt8(value)))}return identifier}()/// Maps an identifier to a Device. If the identifier can not be mapped to an existing device, `UnknownDevice(identifier)` is returned.////// - parameter identifier: The device identifier, e.g. "iPhone7,1". Can be obtained from `Device.identifier`.////// - returns: An initialized `Device`.public static func mapToDevice(identifier: String) -> Device { // swiftlint:disable:this cyclomatic_complexity function_body_length#if os(iOS)switch identifier {case "iPod5,1": return iPodTouch5case "iPod7,1": return iPodTouch6case "iPod9,1": return iPodTouch7case "iPhone3,1", "iPhone3,2", "iPhone3,3": return iPhone4case "iPhone4,1": return iPhone4scase "iPhone5,1", "iPhone5,2": return iPhone5case "iPhone5,3", "iPhone5,4": return iPhone5ccase "iPhone6,1", "iPhone6,2": return iPhone5scase "iPhone7,2": return iPhone6case "iPhone7,1": return iPhone6Pluscase "iPhone8,1": return iPhone6scase "iPhone8,2": return iPhone6sPluscase "iPhone9,1", "iPhone9,3": return iPhone7case "iPhone9,2", "iPhone9,4": return iPhone7Pluscase "iPhone8,4": return iPhoneSEcase "iPhone10,1", "iPhone10,4": return iPhone8case "iPhone10,2", "iPhone10,5": return iPhone8Pluscase "iPhone10,3", "iPhone10,6": return iPhoneXcase "iPhone11,2": return iPhoneXScase "iPhone11,4", "iPhone11,6": return iPhoneXSMaxcase "iPhone11,8": return iPhoneXRcase "iPhone12,1": return iPhone11case "iPhone12,3": return iPhone11Procase "iPhone12,5": return iPhone11ProMaxcase "iPhone12,8": return iPhoneSE2case "iPhone13,2": return iPhone12case "iPhone13,1": return iPhone12Minicase "iPhone13,3": return iPhone12Procase "iPhone13,4": return iPhone12ProMaxcase "iPhone14,5": return iPhone13case "iPhone14,4": return iPhone13Minicase "iPhone14,2": return iPhone13Procase "iPhone14,3": return iPhone13ProMaxcase "iPhone14,6": return iPhoneSE3case "iPad2,1", "iPad2,2", "iPad2,3", "iPad2,4": return iPad2case "iPad3,1", "iPad3,2", "iPad3,3": return iPad3case "iPad3,4", "iPad3,5", "iPad3,6": return iPad4case "iPad4,1", "iPad4,2", "iPad4,3": return iPadAircase "iPad5,3", "iPad5,4": return iPadAir2case "iPad6,11", "iPad6,12": return iPad5case "iPad7,5", "iPad7,6": return iPad6case "iPad11,3", "iPad11,4": return iPadAir3case "iPad7,11", "iPad7,12": return iPad7case "iPad11,6", "iPad11,7": return iPad8case "iPad12,1", "iPad12,2": return iPad9case "iPad13,1", "iPad13,2": return iPadAir4case "iPad13,16", "iPad13,17": return iPadAir5case "iPad2,5", "iPad2,6", "iPad2,7": return iPadMinicase "iPad4,4", "iPad4,5", "iPad4,6": return iPadMini2case "iPad4,7", "iPad4,8", "iPad4,9": return iPadMini3case "iPad5,1", "iPad5,2": return iPadMini4case "iPad11,1", "iPad11,2": return iPadMini5case "iPad14,1", "iPad14,2": return iPadMini6case "iPad6,3", "iPad6,4": return iPadPro9Inchcase "iPad6,7", "iPad6,8": return iPadPro12Inchcase "iPad7,1", "iPad7,2": return iPadPro12Inch2case "iPad7,3", "iPad7,4": return iPadPro10Inchcase "iPad8,1", "iPad8,2", "iPad8,3", "iPad8,4": return iPadPro11Inchcase "iPad8,5", "iPad8,6", "iPad8,7", "iPad8,8": return iPadPro12Inch3case "iPad8,9", "iPad8,10": return iPadPro11Inch2case "iPad8,11", "iPad8,12": return iPadPro12Inch4case "iPad13,4", "iPad13,5", "iPad13,6", "iPad13,7": return iPadPro11Inch3case "iPad13,8", "iPad13,9", "iPad13,10", "iPad13,11": return iPadPro12Inch5case "AudioAccessory1,1": return homePodcase "i386", "x86_64", "arm64": return simulator(mapToDevice(identifier: ProcessInfo().environment["SIMULATOR_MODEL_IDENTIFIER"] ?? "iOS"))default: return unknown(identifier)}#elseif os(tvOS)switch identifier {case "AppleTV5,3": return appleTVHDcase "AppleTV6,2": return appleTV4Kcase "AppleTV11,1": return appleTV4K2case "i386", "x86_64", "arm64": return simulator(mapToDevice(identifier: ProcessInfo().environment["SIMULATOR_MODEL_IDENTIFIER"] ?? "tvOS"))default: return unknown(identifier)}#elseif os(watchOS)switch identifier {case "Watch1,1": return appleWatchSeries0_38mmcase "Watch1,2": return appleWatchSeries0_42mmcase "Watch2,6": return appleWatchSeries1_38mmcase "Watch2,7": return appleWatchSeries1_42mmcase "Watch2,3": return appleWatchSeries2_38mmcase "Watch2,4": return appleWatchSeries2_42mmcase "Watch3,1", "Watch3,3": return appleWatchSeries3_38mmcase "Watch3,2", "Watch3,4": return appleWatchSeries3_42mmcase "Watch4,1", "Watch4,3": return appleWatchSeries4_40mmcase "Watch4,2", "Watch4,4": return appleWatchSeries4_44mmcase "Watch5,1", "Watch5,3": return appleWatchSeries5_40mmcase "Watch5,2", "Watch5,4": return appleWatchSeries5_44mmcase "Watch6,1", "Watch6,3": return appleWatchSeries6_40mmcase "Watch6,2", "Watch6,4": return appleWatchSeries6_44mmcase "Watch5,9", "Watch5,11": return appleWatchSE_40mmcase "Watch5,10", "Watch5,12": return appleWatchSE_44mmcase "i386", "x86_64", "arm64": return simulator(mapToDevice(identifier: ProcessInfo().environment["SIMULATOR_MODEL_IDENTIFIER"] ?? "watchOS"))default: return unknown(identifier)}#endif}/// Get the real device from a device./// If the device is a an iPhone8Plus simulator this function returns .iPhone8Plus (the real device)./// If the parameter is a real device, this function returns just that passed parameter.////// - parameter device: A device.////// - returns: the underlying device If the `device` is a `simulator`,/// otherwise return the `device`.public static func realDevice(from device: Device) -> Device {if case let .simulator(model) = device {return model}return device}#if os(iOS) || os(watchOS)/// Returns diagonal screen length in inchespublic var diagonal: Double {#if os(iOS)switch self {case .iPodTouch5: return 4case .iPodTouch6: return 4case .iPodTouch7: return 4case .iPhone4: return 3.5case .iPhone4s: return 3.5case .iPhone5: return 4case .iPhone5c: return 4case .iPhone5s: return 4case .iPhone6: return 4.7case .iPhone6Plus: return 5.5case .iPhone6s: return 4.7case .iPhone6sPlus: return 5.5case .iPhone7: return 4.7case .iPhone7Plus: return 5.5case .iPhoneSE: return 4case .iPhone8: return 4.7case .iPhone8Plus: return 5.5case .iPhoneX: return 5.8case .iPhoneXS: return 5.8case .iPhoneXSMax: return 6.5case .iPhoneXR: return 6.1case .iPhone11: return 6.1case .iPhone11Pro: return 5.8case .iPhone11ProMax: return 6.5case .iPhoneSE2: return 4.7case .iPhone12: return 6.1case .iPhone12Mini: return 5.4case .iPhone12Pro: return 6.1case .iPhone12ProMax: return 6.7case .iPhone13: return 6.1case .iPhone13Mini: return 5.4case .iPhone13Pro: return 6.1case .iPhone13ProMax: return 6.7case .iPhoneSE3: return 4.7case .iPad2: return 9.7case .iPad3: return 9.7case .iPad4: return 9.7case .iPadAir: return 9.7case .iPadAir2: return 9.7case .iPad5: return 9.7case .iPad6: return 9.7case .iPadAir3: return 10.5case .iPad7: return 10.2case .iPad8: return 10.2case .iPad9: return 10.2case .iPadAir4: return 10.9case .iPadAir5: return 10.9case .iPadMini: return 7.9case .iPadMini2: return 7.9case .iPadMini3: return 7.9case .iPadMini4: return 7.9case .iPadMini5: return 7.9case .iPadMini6: return 8.3case .iPadPro9Inch: return 9.7case .iPadPro12Inch: return 12.9case .iPadPro12Inch2: return 12.9case .iPadPro10Inch: return 10.5case .iPadPro11Inch: return 11.0case .iPadPro12Inch3: return 12.9case .iPadPro11Inch2: return 11.0case .iPadPro12Inch4: return 12.9case .iPadPro11Inch3: return 11.0case .iPadPro12Inch5: return 12.9case .homePod: return -1case .simulator(let model): return model.diagonalcase .unknown: return -1}#elseif os(watchOS)switch self {case .appleWatchSeries0_38mm: return 1.5case .appleWatchSeries0_42mm: return 1.6case .appleWatchSeries1_38mm: return 1.5case .appleWatchSeries1_42mm: return 1.6case .appleWatchSeries2_38mm: return 1.5case .appleWatchSeries2_42mm: return 1.6case .appleWatchSeries3_38mm: return 1.5case .appleWatchSeries3_42mm: return 1.6case .appleWatchSeries4_40mm: return 1.8case .appleWatchSeries4_44mm: return 2.0case .appleWatchSeries5_40mm: return 1.8case .appleWatchSeries5_44mm: return 2.0case .appleWatchSeries6_40mm: return 1.8case .appleWatchSeries6_44mm: return 2.0case .appleWatchSE_40mm: return 1.8case .appleWatchSE_44mm: return 2.0case .simulator(let model): return model.diagonalcase .unknown: return -1}#endif}#endif/// Returns screen ratio as a tuplepublic var screenRatio: (width: Double, height: Double) {#if os(iOS)switch self {case .iPodTouch5: return (width: 9, height: 16)case .iPodTouch6: return (width: 9, height: 16)case .iPodTouch7: return (width: 9, height: 16)case .iPhone4: return (width: 2, height: 3)case .iPhone4s: return (width: 2, height: 3)case .iPhone5: return (width: 9, height: 16)case .iPhone5c: return (width: 9, height: 16)case .iPhone5s: return (width: 9, height: 16)case .iPhone6: return (width: 9, height: 16)case .iPhone6Plus: return (width: 9, height: 16)case .iPhone6s: return (width: 9, height: 16)case .iPhone6sPlus: return (width: 9, height: 16)case .iPhone7: return (width: 9, height: 16)case .iPhone7Plus: return (width: 9, height: 16)case .iPhoneSE: return (width: 9, height: 16)case .iPhone8: return (width: 9, height: 16)case .iPhone8Plus: return (width: 9, height: 16)case .iPhoneX: return (width: 9, height: 19.5)case .iPhoneXS: return (width: 9, height: 19.5)case .iPhoneXSMax: return (width: 9, height: 19.5)case .iPhoneXR: return (width: 9, height: 19.5)case .iPhone11: return (width: 9, height: 19.5)case .iPhone11Pro: return (width: 9, height: 19.5)case .iPhone11ProMax: return (width: 9, height: 19.5)case .iPhoneSE2: return (width: 9, height: 16)case .iPhone12: return (width: 9, height: 19.5)case .iPhone12Mini: return (width: 9, height: 19.5)case .iPhone12Pro: return (width: 9, height: 19.5)case .iPhone12ProMax: return (width: 9, height: 19.5)case .iPhone13: return (width: 9, height: 19.5)case .iPhone13Mini: return (width: 9, height: 19.5)case .iPhone13Pro: return (width: 9, height: 19.5)case .iPhone13ProMax: return (width: 9, height: 19.5)case .iPhoneSE3: return (width: 9, height: 16)case .iPad2: return (width: 3, height: 4)case .iPad3: return (width: 3, height: 4)case .iPad4: return (width: 3, height: 4)case .iPadAir: return (width: 3, height: 4)case .iPadAir2: return (width: 3, height: 4)case .iPad5: return (width: 3, height: 4)case .iPad6: return (width: 3, height: 4)case .iPadAir3: return (width: 3, height: 4)case .iPad7: return (width: 3, height: 4)case .iPad8: return (width: 3, height: 4)case .iPad9: return (width: 3, height: 4)case .iPadAir4: return (width: 41, height: 59)case .iPadAir5: return (width: 41, height: 59)case .iPadMini: return (width: 3, height: 4)case .iPadMini2: return (width: 3, height: 4)case .iPadMini3: return (width: 3, height: 4)case .iPadMini4: return (width: 3, height: 4)case .iPadMini5: return (width: 3, height: 4)case .iPadMini6: return (width: 744, height: 1133)case .iPadPro9Inch: return (width: 3, height: 4)case .iPadPro12Inch: return (width: 3, height: 4)case .iPadPro12Inch2: return (width: 3, height: 4)case .iPadPro10Inch: return (width: 3, height: 4)case .iPadPro11Inch: return (width: 139, height: 199)case .iPadPro12Inch3: return (width: 512, height: 683)case .iPadPro11Inch2: return (width: 139, height: 199)case .iPadPro12Inch4: return (width: 512, height: 683)case .iPadPro11Inch3: return (width: 139, height: 199)case .iPadPro12Inch5: return (width: 512, height: 683)case .homePod: return (width: 4, height: 5)case .simulator(let model): return model.screenRatiocase .unknown: return (width: -1, height: -1)}#elseif os(watchOS)switch self {case .appleWatchSeries0_38mm: return (width: 4, height: 5)case .appleWatchSeries0_42mm: return (width: 4, height: 5)case .appleWatchSeries1_38mm: return (width: 4, height: 5)case .appleWatchSeries1_42mm: return (width: 4, height: 5)case .appleWatchSeries2_38mm: return (width: 4, height: 5)case .appleWatchSeries2_42mm: return (width: 4, height: 5)case .appleWatchSeries3_38mm: return (width: 4, height: 5)case .appleWatchSeries3_42mm: return (width: 4, height: 5)case .appleWatchSeries4_40mm: return (width: 4, height: 5)case .appleWatchSeries4_44mm: return (width: 4, height: 5)case .appleWatchSeries5_40mm: return (width: 4, height: 5)case .appleWatchSeries5_44mm: return (width: 4, height: 5)case .appleWatchSeries6_40mm: return (width: 4, height: 5)case .appleWatchSeries6_44mm: return (width: 4, height: 5)case .appleWatchSE_40mm: return (width: 4, height: 5)case .appleWatchSE_44mm: return (width: 4, height: 5)case .simulator(let model): return model.screenRatiocase .unknown: return (width: -1, height: -1)}#elseif os(tvOS)return (width: -1, height: -1)#endif}#if os(iOS)/// All iPodspublic static var allPods: [Device] {return [.iPodTouch5, .iPodTouch6, .iPodTouch7]}/// All iPhonespublic 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]}/// All iPadspublic static var allPads: [Device] {return [.iPad2, .iPad3, .iPad4, .iPadAir, .iPadAir2, .iPad5, .iPad6, .iPadAir3, .iPad7, .iPad8, .iPad9, .iPadAir4, .iPadAir5, .iPadMini, .iPadMini2, .iPadMini3, .iPadMini4, .iPadMini5, .iPadMini6, .iPadPro9Inch, .iPadPro12Inch, .iPadPro12Inch2, .iPadPro10Inch, .iPadPro11Inch, .iPadPro12Inch3, .iPadPro11Inch2, .iPadPro12Inch4, .iPadPro11Inch3, .iPadPro12Inch5]}/// 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]}/// All Plus and Max-Sized Devicespublic static var allPlusSizedDevices: [Device] {return [.iPhone6Plus, .iPhone6sPlus, .iPhone7Plus, .iPhone8Plus, .iPhoneXSMax, .iPhone11ProMax, .iPhone12ProMax, .iPhone13ProMax]}/// All Pro Devicespublic static var allProDevices: [Device] {return [.iPhone11Pro, .iPhone11ProMax, .iPhone12Pro, .iPhone12ProMax, .iPhone13Pro, .iPhone13ProMax, .iPadPro9Inch, .iPadPro12Inch, .iPadPro12Inch2, .iPadPro10Inch, .iPadPro11Inch, .iPadPro12Inch3, .iPadPro11Inch2, .iPadPro12Inch4, .iPadPro11Inch3, .iPadPro12Inch5]}/// All mini Devicespublic static var allMiniDevices: [Device] {return [.iPadMini, .iPadMini2, .iPadMini3, .iPadMini4, .iPadMini5, .iPadMini6]}/// All simulator iPodspublic static var allSimulatorPods: [Device] {return allPods.map(Device.simulator)}/// All simulator iPhonespublic static var allSimulatorPhones: [Device] {return allPhones.map(Device.simulator)}/// All simulator iPadspublic static var allSimulatorPads: [Device] {return allPads.map(Device.simulator)}/// All simulator iPad minipublic static var allSimulatorMiniDevices: [Device] {return allMiniDevices.map(Device.simulator)}/// All simulator X series Devices@available(*, deprecated, renamed: "allSimulatorDevicesWithSensorHousing")public static var allSimulatorXSeriesDevices: [Device] {return allDevicesWithSensorHousing.map(Device.simulator)}/// All simulator Plus and Max-Sized Devicespublic static var allSimulatorPlusSizedDevices: [Device] {return allPlusSizedDevices.map(Device.simulator)}/// All simulator Pro Devicespublic static var allSimulatorProDevices: [Device] {return allProDevices.map(Device.simulator)}/// Returns whether the device is an iPod (real or simulator)public var isPod: Bool {return isOneOf(Device.allPods) || isOneOf(Device.allSimulatorPods)}/// Returns whether the device is an iPhone (real or simulator)public var isPhone: Bool {return (isOneOf(Device.allPhones)|| isOneOf(Device.allSimulatorPhones)|| (UIDevice.current.userInterfaceIdiom == .phone && isCurrent)) && !isPod}/// Returns whether the device is an iPad (real or simulator)public var isPad: Bool {return isOneOf(Device.allPads)|| isOneOf(Device.allSimulatorPads)|| (UIDevice.current.userInterfaceIdiom == .pad && isCurrent)}/// Returns whether the device is any of the simulator/// Useful when there is a need to check and skip running a portion of code (location request or others)public var isSimulator: Bool {return isOneOf(Device.allSimulators)}/// If this device is a simulator return the underlying device,/// otherwise return `self`.public var realDevice: Device {return Device.realDevice(from: self)}public var isZoomed: Bool? {guard isCurrent else { return nil }if Int(UIScreen.main.scale.rounded()) == 3 {// Plus-sizedreturn UIScreen.main.nativeScale > 2.7 && UIScreen.main.nativeScale < 3} else {return UIScreen.main.nativeScale > UIScreen.main.scale}}/// All Touch ID Capable Devicespublic static var allTouchIDCapableDevices: [Device] {return [.iPhone5s, .iPhone6, .iPhone6Plus, .iPhone6s, .iPhone6sPlus, .iPhone7, .iPhone7Plus, .iPhoneSE, .iPhone8, .iPhone8Plus, .iPhoneSE2, .iPhoneSE3, .iPadAir2, .iPad5, .iPad6, .iPadAir3, .iPad7, .iPad8, .iPad9, .iPadAir4, .iPadAir5, .iPadMini3, .iPadMini4, .iPadMini5, .iPadMini6, .iPadPro9Inch, .iPadPro12Inch, .iPadPro12Inch2, .iPadPro10Inch]}/// All Face ID Capable Devicespublic static var allFaceIDCapableDevices: [Device] {return [.iPhoneX, .iPhoneXS, .iPhoneXSMax, .iPhoneXR, .iPhone11, .iPhone11Pro, .iPhone11ProMax, .iPhone12, .iPhone12Mini, .iPhone12Pro, .iPhone12ProMax, .iPhone13, .iPhone13Mini, .iPhone13Pro, .iPhone13ProMax, .iPadPro11Inch, .iPadPro12Inch3, .iPadPro11Inch2, .iPadPro12Inch4, .iPadPro11Inch3, .iPadPro12Inch5]}/// All Devices with Touch ID or Face IDpublic 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, .iPadAir2, .iPad5, .iPad6, .iPadAir3, .iPad7, .iPad8, .iPad9, .iPadAir4, .iPadAir5, .iPadMini3, .iPadMini4, .iPadMini5, .iPadMini6, .iPadPro9Inch, .iPadPro12Inch, .iPadPro12Inch2, .iPadPro10Inch, .iPadPro11Inch, .iPadPro12Inch3, .iPadPro11Inch2, .iPadPro12Inch4, .iPadPro11Inch3, .iPadPro12Inch5]}/// Returns whether or not the device has Touch IDpublic var isTouchIDCapable: Bool {return isOneOf(Device.allTouchIDCapableDevices) || isOneOf(Device.allTouchIDCapableDevices.map(Device.simulator))}/// Returns whether or not the device has Face IDpublic var isFaceIDCapable: Bool {return isOneOf(Device.allFaceIDCapableDevices) || isOneOf(Device.allFaceIDCapableDevices.map(Device.simulator))}/// Returns whether or not the device has any biometric sensor (i.e. Touch ID or Face ID)public var hasBiometricSensor: Bool {return isTouchIDCapable || isFaceIDCapable}/// All devices that feature a sensor housing in the screenpublic static var allDevicesWithSensorHousing: [Device] {return [.iPhoneX, .iPhoneXS, .iPhoneXSMax, .iPhoneXR, .iPhone11, .iPhone11Pro, .iPhone11ProMax, .iPhone12, .iPhone12Mini, .iPhone12Pro, .iPhone12ProMax, .iPhone13, .iPhone13Mini, .iPhone13Pro, .iPhone13ProMax]}/// All simulator devices that feature a sensor housing in the screenpublic static var allSimulatorDevicesWithSensorHousing: [Device] {return allDevicesWithSensorHousing.map(Device.simulator)}/// Returns whether or not the device has a sensor housingpublic var hasSensorHousing: Bool {return isOneOf(Device.allDevicesWithSensorHousing) || isOneOf(Device.allDevicesWithSensorHousing.map(Device.simulator))}/// 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, .iPadAir4, .iPadAir5, .iPadMini6, .iPadPro11Inch, .iPadPro12Inch3, .iPadPro11Inch2, .iPadPro12Inch4, .iPadPro11Inch3, .iPadPro12Inch5]}/// Returns whether or not the device has a screen with rounded corners.public var hasRoundedDisplayCorners: Bool {return isOneOf(Device.allDevicesWithRoundedDisplayCorners) || isOneOf(Device.allDevicesWithRoundedDisplayCorners.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]}/// Returns whether or not the device has 3D Touch support.public var has3dTouchSupport: Bool {return isOneOf(Device.allDevicesWith3dTouchSupport) || isOneOf(Device.allDevicesWith3dTouchSupport.map(Device.simulator))}/// 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]}/// Returns whether or not the device supports wireless charging.public var supportsWirelessCharging: Bool {return isOneOf(Device.allDevicesWithWirelessChargingSupport) || isOneOf(Device.allDevicesWithWirelessChargingSupport.map(Device.simulator))}/// All devices that have a LiDAR sensor.public static var allDevicesWithALidarSensor: [Device] {return [.iPhone12Pro, .iPhone12ProMax, .iPhone13Pro, .iPhone13ProMax, .iPadPro11Inch2, .iPadPro12Inch4, .iPadPro11Inch3, .iPadPro12Inch5]}/// Returns whether or not the device has a LiDAR sensor.public var hasLidarSensor: Bool {return isOneOf(Device.allDevicesWithALidarSensor) || isOneOf(Device.allDevicesWithALidarSensor.map(Device.simulator))}#elseif os(tvOS)/// All TVspublic static var allTVs: [Device] {return [.appleTVHD, .appleTV4K, .appleTV4K2]}/// All simulator TVspublic static var allSimulatorTVs: [Device] {return allTVs.map(Device.simulator)}#elseif os(watchOS)/// All Watchespublic 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]}/// All simulator Watchespublic static var allSimulatorWatches: [Device] {return allWatches.map(Device.simulator)}/// All watches that have Force Touch support.public static var allWatchesWithForceTouchSupport: [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]}/// Returns whether or not the device has Force Touch support.public var hasForceTouchSupport: Bool {return isOneOf(Device.allWatchesWithForceTouchSupport) || isOneOf(Device.allWatchesWithForceTouchSupport.map(Device.simulator))}#endif/// All real devices (i.e. all devices except for all simulators)public static var allRealDevices: [Device] {#if os(iOS)return allPods + allPhones + allPads#elseif os(tvOS)return allTVs#elseif os(watchOS)return allWatches#endif}/// All simulatorspublic static var allSimulators: [Device] {return allRealDevices.map(Device.simulator)}/**This method saves you in many cases from the need of updating your code with every new device.Most uses for an enum like this are the following:```switch Device.current {case .iPodTouch5, .iPodTouch6: callMethodOnIPods()case .iPhone4, iPhone4s, .iPhone5, .iPhone5s, .iPhone6, .iPhone6Plus, .iPhone6s, .iPhone6sPlus, .iPhone7, .iPhone7Plus, .iPhoneSE, .iPhone8, .iPhone8Plus, .iPhoneX: callMethodOnIPhones()case .iPad2, .iPad3, .iPad4, .iPadAir, .iPadAir2, .iPadMini, .iPadMini2, .iPadMini3, .iPadMini4, .iPadPro: callMethodOnIPads()default: break}```This code can now be replaced with```let device = Device.currentif device.isOneOf(Device.allPods) {callMethodOnIPods()} else if device.isOneOf(Device.allPhones) {callMethodOnIPhones()} else if device.isOneOf(Device.allPads) {callMethodOnIPads()}```- parameter devices: An array of devices.- returns: Returns whether the current device is one of the passed in ones.*/public func isOneOf(_ devices: [Device]) -> Bool {return devices.contains(self)}// MARK: Current Device/// Whether or not the current device is the current device.private var isCurrent: Bool {return self == Device.current}/// The name identifying the device (e.g. "Dennis' iPhone").public var name: String? {guard isCurrent else { return nil }#if os(watchOS)return WKInterfaceDevice.current().name#elsereturn UIDevice.current.name#endif}/// The name of the operating system running on the device represented by the receiver (e.g. "iOS" or "tvOS").public var systemName: String? {guard isCurrent else { return nil }#if os(watchOS)return WKInterfaceDevice.current().systemName#elsereturn UIDevice.current.systemName#endif}/// The current version of the operating system (e.g. 8.4 or 9.2).public var systemVersion: String? {guard isCurrent else { return nil }#if os(watchOS)return WKInterfaceDevice.current().systemVersion#elsereturn UIDevice.current.systemVersion#endif}/// The model of the device (e.g. "iPhone" or "iPod Touch").public var model: String? {guard isCurrent else { return nil }#if os(watchOS)return WKInterfaceDevice.current().model#elsereturn UIDevice.current.model#endif}/// The model of the device as a localized string.public var localizedModel: String? {guard isCurrent else { return nil }#if os(watchOS)return WKInterfaceDevice.current().localizedModel#elsereturn UIDevice.current.localizedModel#endif}/// PPI (Pixels per Inch) on the current device's screen (if applicable). When the device is not applicable this property returns nil.public var ppi: Int? {#if os(iOS)switch self {case .iPodTouch5: return 326case .iPodTouch6: return 326case .iPodTouch7: return 326case .iPhone4: return 326case .iPhone4s: return 326case .iPhone5: return 326case .iPhone5c: return 326case .iPhone5s: return 326case .iPhone6: return 326case .iPhone6Plus: return 401case .iPhone6s: return 326case .iPhone6sPlus: return 401case .iPhone7: return 326case .iPhone7Plus: return 401case .iPhoneSE: return 326case .iPhone8: return 326case .iPhone8Plus: return 401case .iPhoneX: return 458case .iPhoneXS: return 458case .iPhoneXSMax: return 458case .iPhoneXR: return 326case .iPhone11: return 326case .iPhone11Pro: return 458case .iPhone11ProMax: return 458case .iPhoneSE2: return 326case .iPhone12: return 460case .iPhone12Mini: return 476case .iPhone12Pro: return 460case .iPhone12ProMax: return 458case .iPhone13: return 460case .iPhone13Mini: return 476case .iPhone13Pro: return 460case .iPhone13ProMax: return 458case .iPhoneSE3: return 326case .iPad2: return 132case .iPad3: return 264case .iPad4: return 264case .iPadAir: return 264case .iPadAir2: return 264case .iPad5: return 264case .iPad6: return 264case .iPadAir3: return 264case .iPad7: return 264case .iPad8: return 264case .iPad9: return 264case .iPadAir4: return 264case .iPadAir5: return 264case .iPadMini: return 163case .iPadMini2: return 326case .iPadMini3: return 326case .iPadMini4: return 326case .iPadMini5: return 326case .iPadMini6: return 326case .iPadPro9Inch: return 264case .iPadPro12Inch: return 264case .iPadPro12Inch2: return 264case .iPadPro10Inch: return 264case .iPadPro11Inch: return 264case .iPadPro12Inch3: return 264case .iPadPro11Inch2: return 264case .iPadPro12Inch4: return 264case .iPadPro11Inch3: return 264case .iPadPro12Inch5: return 264case .homePod: return -1case .simulator(let model): return model.ppicase .unknown: return nil}#elseif os(watchOS)switch self {case .appleWatchSeries0_38mm: return 290case .appleWatchSeries0_42mm: return 303case .appleWatchSeries1_38mm: return 290case .appleWatchSeries1_42mm: return 303case .appleWatchSeries2_38mm: return 290case .appleWatchSeries2_42mm: return 303case .appleWatchSeries3_38mm: return 290case .appleWatchSeries3_42mm: return 303case .appleWatchSeries4_40mm: return 326case .appleWatchSeries4_44mm: return 326case .appleWatchSeries5_40mm: return 326case .appleWatchSeries5_44mm: return 326case .appleWatchSeries6_40mm: return 326case .appleWatchSeries6_44mm: return 326case .appleWatchSE_40mm: return 326case .appleWatchSE_44mm: return 326case .simulator(let model): return model.ppicase .unknown: return nil}#elseif os(tvOS)return nil#endif}/// True when a Guided Access session is currently active; otherwise, false.public var isGuidedAccessSessionActive: Bool {#if os(iOS)#if swift(>=4.2)return UIAccessibility.isGuidedAccessEnabled#elsereturn UIAccessibilityIsGuidedAccessEnabled()#endif#elsereturn false#endif}/// The brightness level of the screen.public var screenBrightness: Int {#if os(iOS)return Int(UIScreen.main.brightness * 100)#elsereturn 100#endif}}// MARK: CustomStringConvertibleextension Device: CustomStringConvertible {/// A textual representation of the device.public var description: String {#if os(iOS)switch self {case .iPodTouch5: return "iPod touch (5th generation)"case .iPodTouch6: return "iPod touch (6th generation)"case .iPodTouch7: return "iPod touch (7th generation)"case .iPhone4: return "iPhone 4"case .iPhone4s: return "iPhone 4s"case .iPhone5: return "iPhone 5"case .iPhone5c: return "iPhone 5c"case .iPhone5s: return "iPhone 5s"case .iPhone6: return "iPhone 6"case .iPhone6Plus: return "iPhone 6 Plus"case .iPhone6s: return "iPhone 6s"case .iPhone6sPlus: return "iPhone 6s Plus"case .iPhone7: return "iPhone 7"case .iPhone7Plus: return "iPhone 7 Plus"case .iPhoneSE: return "iPhone SE"case .iPhone8: return "iPhone 8"case .iPhone8Plus: return "iPhone 8 Plus"case .iPhoneX: return "iPhone X"case .iPhoneXS: return "iPhone Xs"case .iPhoneXSMax: return "iPhone Xs Max"case .iPhoneXR: return "iPhone XÊ€"case .iPhone11: return "iPhone 11"case .iPhone11Pro: return "iPhone 11 Pro"case .iPhone11ProMax: return "iPhone 11 Pro Max"case .iPhoneSE2: return "iPhone SE (2nd generation)"case .iPhone12: return "iPhone 12"case .iPhone12Mini: return "iPhone 12 mini"case .iPhone12Pro: return "iPhone 12 Pro"case .iPhone12ProMax: return "iPhone 12 Pro Max"case .iPhone13: return "iPhone 13"case .iPhone13Mini: return "iPhone 13 mini"case .iPhone13Pro: return "iPhone 13 Pro"case .iPhone13ProMax: return "iPhone 13 Pro Max"case .iPhoneSE3: return "iPhone SE (3rd generation)"case .iPad2: return "iPad 2"case .iPad3: return "iPad (3rd generation)"case .iPad4: return "iPad (4th generation)"case .iPadAir: return "iPad Air"case .iPadAir2: return "iPad Air 2"case .iPad5: return "iPad (5th generation)"case .iPad6: return "iPad (6th generation)"case .iPadAir3: return "iPad Air (3rd generation)"case .iPad7: return "iPad (7th generation)"case .iPad8: return "iPad (8th generation)"case .iPad9: return "iPad (9th generation)"case .iPadAir4: return "iPad Air (4th generation)"case .iPadAir5: return "iPad Air (5th generation)"case .iPadMini: return "iPad Mini"case .iPadMini2: return "iPad Mini 2"case .iPadMini3: return "iPad Mini 3"case .iPadMini4: return "iPad Mini 4"case .iPadMini5: return "iPad Mini (5th generation)"case .iPadMini6: return "iPad Mini (6th generation)"case .iPadPro9Inch: return "iPad Pro (9.7-inch)"case .iPadPro12Inch: return "iPad Pro (12.9-inch)"case .iPadPro12Inch2: return "iPad Pro (12.9-inch) (2nd generation)"case .iPadPro10Inch: return "iPad Pro (10.5-inch)"case .iPadPro11Inch: return "iPad Pro (11-inch)"case .iPadPro12Inch3: return "iPad Pro (12.9-inch) (3rd generation)"case .iPadPro11Inch2: return "iPad Pro (11-inch) (2nd generation)"case .iPadPro12Inch4: return "iPad Pro (12.9-inch) (4th generation)"case .iPadPro11Inch3: return "iPad Pro (11-inch) (3rd generation)"case .iPadPro12Inch5: return "iPad Pro (12.9-inch) (5th generation)"case .homePod: return "HomePod"case .simulator(let model): return "Simulator (\(model.description))"case .unknown(let identifier): return identifier}#elseif os(watchOS)switch self {case .appleWatchSeries0_38mm: return "Apple Watch (1st generation) 38mm"case .appleWatchSeries0_42mm: return "Apple Watch (1st generation) 42mm"case .appleWatchSeries1_38mm: return "Apple Watch Series 1 38mm"case .appleWatchSeries1_42mm: return "Apple Watch Series 1 42mm"case .appleWatchSeries2_38mm: return "Apple Watch Series 2 38mm"case .appleWatchSeries2_42mm: return "Apple Watch Series 2 42mm"case .appleWatchSeries3_38mm: return "Apple Watch Series 3 38mm"case .appleWatchSeries3_42mm: return "Apple Watch Series 3 42mm"case .appleWatchSeries4_40mm: return "Apple Watch Series 4 40mm"case .appleWatchSeries4_44mm: return "Apple Watch Series 4 44mm"case .appleWatchSeries5_40mm: return "Apple Watch Series 5 40mm"case .appleWatchSeries5_44mm: return "Apple Watch Series 5 44mm"case .appleWatchSeries6_40mm: return "Apple Watch Series 6 40mm"case .appleWatchSeries6_44mm: return "Apple Watch Series 6 44mm"case .appleWatchSE_40mm: return "Apple Watch SE 40mm"case .appleWatchSE_44mm: return "Apple Watch SE 44mm"case .simulator(let model): return "Simulator (\(model.description))"case .unknown(let identifier): return identifier}#elseif os(tvOS)switch self {case .appleTVHD: return "Apple TV HD"case .appleTV4K: return "Apple TV 4K"case .appleTV4K2: return "Apple TV 4K (2nd generation)"case .simulator(let model): return "Simulator (\(model.description))"case .unknown(let identifier): return identifier}#endif}/// A safe version of `description`./// Example:/// Device.iPhoneXR.description: iPhone XÊ€/// Device.iPhoneXR.safeDescription: iPhone XRpublic var safeDescription: String {#if os(iOS)switch self {case .iPodTouch5: return "iPod touch (5th generation)"case .iPodTouch6: return "iPod touch (6th generation)"case .iPodTouch7: return "iPod touch (7th generation)"case .iPhone4: return "iPhone 4"case .iPhone4s: return "iPhone 4s"case .iPhone5: return "iPhone 5"case .iPhone5c: return "iPhone 5c"case .iPhone5s: return "iPhone 5s"case .iPhone6: return "iPhone 6"case .iPhone6Plus: return "iPhone 6 Plus"case .iPhone6s: return "iPhone 6s"case .iPhone6sPlus: return "iPhone 6s Plus"case .iPhone7: return "iPhone 7"case .iPhone7Plus: return "iPhone 7 Plus"case .iPhoneSE: return "iPhone SE"case .iPhone8: return "iPhone 8"case .iPhone8Plus: return "iPhone 8 Plus"case .iPhoneX: return "iPhone X"case .iPhoneXS: return "iPhone XS"case .iPhoneXSMax: return "iPhone XS Max"case .iPhoneXR: return "iPhone XR"case .iPhone11: return "iPhone 11"case .iPhone11Pro: return "iPhone 11 Pro"case .iPhone11ProMax: return "iPhone 11 Pro Max"case .iPhoneSE2: return "iPhone SE (2nd generation)"case .iPhone12: return "iPhone 12"case .iPhone12Mini: return "iPhone 12 mini"case .iPhone12Pro: return "iPhone 12 Pro"case .iPhone12ProMax: return "iPhone 12 Pro Max"case .iPhone13: return "iPhone 13"case .iPhone13Mini: return "iPhone 13 mini"case .iPhone13Pro: return "iPhone 13 Pro"case .iPhone13ProMax: return "iPhone 13 Pro Max"case .iPhoneSE3: return "iPhone SE (3rd generation)"case .iPad2: return "iPad 2"case .iPad3: return "iPad (3rd generation)"case .iPad4: return "iPad (4th generation)"case .iPadAir: return "iPad Air"case .iPadAir2: return "iPad Air 2"case .iPad5: return "iPad (5th generation)"case .iPad6: return "iPad (6th generation)"case .iPadAir3: return "iPad Air (3rd generation)"case .iPad7: return "iPad (7th generation)"case .iPad8: return "iPad (8th generation)"case .iPad9: return "iPad (9th generation)"case .iPadAir4: return "iPad Air (4th generation)"case .iPadAir5: return "iPad Air (5th generation)"case .iPadMini: return "iPad Mini"case .iPadMini2: return "iPad Mini 2"case .iPadMini3: return "iPad Mini 3"case .iPadMini4: return "iPad Mini 4"case .iPadMini5: return "iPad Mini (5th generation)"case .iPadMini6: return "iPad Mini (6th generation)"case .iPadPro9Inch: return "iPad Pro (9.7-inch)"case .iPadPro12Inch: return "iPad Pro (12.9-inch)"case .iPadPro12Inch2: return "iPad Pro (12.9-inch) (2nd generation)"case .iPadPro10Inch: return "iPad Pro (10.5-inch)"case .iPadPro11Inch: return "iPad Pro (11-inch)"case .iPadPro12Inch3: return "iPad Pro (12.9-inch) (3rd generation)"case .iPadPro11Inch2: return "iPad Pro (11-inch) (2nd generation)"case .iPadPro12Inch4: return "iPad Pro (12.9-inch) (4th generation)"case .iPadPro11Inch3: return "iPad Pro (11-inch) (3rd generation)"case .iPadPro12Inch5: return "iPad Pro (12.9-inch) (5th generation)"case .homePod: return "HomePod"case .simulator(let model): return "Simulator (\(model.safeDescription))"case .unknown(let identifier): return identifier}#elseif os(watchOS)switch self {case .appleWatchSeries0_38mm: return "Apple Watch (1st generation) 38mm"case .appleWatchSeries0_42mm: return "Apple Watch (1st generation) 42mm"case .appleWatchSeries1_38mm: return "Apple Watch Series 1 38mm"case .appleWatchSeries1_42mm: return "Apple Watch Series 1 42mm"case .appleWatchSeries2_38mm: return "Apple Watch Series 2 38mm"case .appleWatchSeries2_42mm: return "Apple Watch Series 2 42mm"case .appleWatchSeries3_38mm: return "Apple Watch Series 3 38mm"case .appleWatchSeries3_42mm: return "Apple Watch Series 3 42mm"case .appleWatchSeries4_40mm: return "Apple Watch Series 4 40mm"case .appleWatchSeries4_44mm: return "Apple Watch Series 4 44mm"case .appleWatchSeries5_40mm: return "Apple Watch Series 5 40mm"case .appleWatchSeries5_44mm: return "Apple Watch Series 5 44mm"case .appleWatchSeries6_40mm: return "Apple Watch Series 6 40mm"case .appleWatchSeries6_44mm: return "Apple Watch Series 6 44mm"case .appleWatchSE_40mm: return "Apple Watch SE 40mm"case .appleWatchSE_44mm: return "Apple Watch SE 44mm"case .simulator(let model): return "Simulator (\(model.safeDescription))"case .unknown(let identifier): return identifier}#elseif os(tvOS)switch self {case .appleTVHD: return "Apple TV HD"case .appleTV4K: return "Apple TV 4K"case .appleTV4K2: return "Apple TV 4K (2nd generation)"case .simulator(let model): return "Simulator (\(model.safeDescription))"case .unknown(let identifier): return identifier}#endif}}// MARK: Equatableextension Device: Equatable {/// Compares two devices////// - parameter lhs: A device./// - parameter rhs: Another device.////// - returns: `true` iff the underlying identifier is the same.public static func == (lhs: Device, rhs: Device) -> Bool {return lhs.description == rhs.description}}// MARK: Battery#if os(iOS) || os(watchOS)@available(iOS 8.0, watchOS 4.0, *)extension Device {/**This enum describes the state of the battery.- Full: The device is plugged into power and the battery is 100% charged or the device is the iOS Simulator.- Charging: The device is plugged into power and the battery is less than 100% charged.- Unplugged: The device is not plugged into power; the battery is discharging.*/public enum BatteryState: CustomStringConvertible, Equatable {/// The device is plugged into power and the battery is 100% charged or the device is the iOS Simulator.case full/// The device is plugged into power and the battery is less than 100% charged./// The associated value is in percent (0-100).case charging(Int)/// The device is not plugged into power; the battery is discharging./// The associated value is in percent (0-100).case unplugged(Int)#if os(iOS)fileprivate init() {let wasBatteryMonitoringEnabled = UIDevice.current.isBatteryMonitoringEnabledUIDevice.current.isBatteryMonitoringEnabled = truelet batteryLevel = Int(round(UIDevice.current.batteryLevel * 100)) // round() is actually not needed anymore since -[batteryLevel] seems to always return a two-digit precision number// but maybe that changes in the future.switch UIDevice.current.batteryState {case .charging: self = .charging(batteryLevel)case .full: self = .fullcase .unplugged: self = .unplugged(batteryLevel)case .unknown: self = .full // Should never happen since `batteryMonitoring` is enabled.@unknown default:self = .full // To cover any future additions for which DeviceKit might not have updated yet.}UIDevice.current.isBatteryMonitoringEnabled = wasBatteryMonitoringEnabled}#elseif os(watchOS)fileprivate init() {let wasBatteryMonitoringEnabled = WKInterfaceDevice.current().isBatteryMonitoringEnabledWKInterfaceDevice.current().isBatteryMonitoringEnabled = truelet batteryLevel = Int(round(WKInterfaceDevice.current().batteryLevel * 100)) // round() is actually not needed anymore since -[batteryLevel] seems to always return a two-digit precision number// but maybe that changes in the future.switch WKInterfaceDevice.current().batteryState {case .charging: self = .charging(batteryLevel)case .full: self = .fullcase .unplugged: self = .unplugged(batteryLevel)case .unknown: self = .full // Should never happen since `batteryMonitoring` is enabled.@unknown default:self = .full // To cover any future additions for which DeviceKit might not have updated yet.}WKInterfaceDevice.current().isBatteryMonitoringEnabled = wasBatteryMonitoringEnabled}#endif/// The user enabled Low Power modepublic var lowPowerMode: Bool {return ProcessInfo.processInfo.isLowPowerModeEnabled}/// Provides a textual representation of the battery state./// Examples:/// ```/// Battery level: 90%, device is plugged in./// Battery level: 100 % (Full), device is plugged in./// Battery level: \(batteryLevel)%, device is unplugged./// ```public var description: String {switch self {case .charging(let batteryLevel): return "Battery level: \(batteryLevel)%, device is plugged in."case .full: return "Battery level: 100 % (Full), device is plugged in."case .unplugged(let batteryLevel): return "Battery level: \(batteryLevel)%, device is unplugged."}}}/// The state of the batterypublic var batteryState: BatteryState? {guard isCurrent else { return nil }return BatteryState()}/// Battery level ranges from 0 (fully discharged) to 100 (100% charged).public var batteryLevel: Int? {guard isCurrent else { return nil }switch BatteryState() {case .charging(let value): return valuecase .full: return 100case .unplugged(let value): return value}}}#endif// MARK: Device.Batterystate: Comparable#if os(iOS) || os(watchOS)@available(iOS 8.0, watchOS 4.0, *)extension Device.BatteryState: Comparable {/// Tells if two battery states are equal.////// - parameter lhs: A battery state./// - parameter rhs: Another battery state.////// - returns: `true` iff they are equal, otherwise `false`public static func == (lhs: Device.BatteryState, rhs: Device.BatteryState) -> Bool {return lhs.description == rhs.description}/// Compares two battery states.////// - parameter lhs: A battery state./// - parameter rhs: Another battery state.////// - returns: `true` if rhs is `.Full`, `false` when lhs is `.Full` otherwise their battery level is compared.public static func < (lhs: Device.BatteryState, rhs: Device.BatteryState) -> Bool {switch (lhs, rhs) {case (.full, _): return false // return false (even if both are `.Full` -> they are equal)case (_, .full): return true // lhs is *not* `.Full`, rhs iscase let (.charging(lhsLevel), .charging(rhsLevel)): return lhsLevel < rhsLevelcase let (.charging(lhsLevel), .unplugged(rhsLevel)): return lhsLevel < rhsLevelcase let (.unplugged(lhsLevel), .charging(rhsLevel)): return lhsLevel < rhsLevelcase let (.unplugged(lhsLevel), .unplugged(rhsLevel)): return lhsLevel < rhsLeveldefault: return false // compiler won't compile without it, though it cannot happen}}}#endif#if os(iOS)extension Device {// MARK: Orientation/**This enum describes the state of the orientation.- Landscape: The device is in Landscape Orientation- Portrait: The device is in Portrait Orientation*/public enum Orientation {case landscapecase portrait}public var orientation: Orientation {if UIDevice.current.orientation.isLandscape {return .landscape} else {return .portrait}}}#endif#if os(iOS)// MARK: DiskSpaceextension Device {/// Return the root url////// - returns: the NSHomeDirectory() urlprivate static let rootURL = URL(fileURLWithPath: NSHomeDirectory())/// The volume’s total capacity in bytes.public static var volumeTotalCapacity: Int? {return (try? Device.rootURL.resourceValues(forKeys: [.volumeTotalCapacityKey]))?.volumeTotalCapacity}/// The volume’s available capacity in bytes.public static var volumeAvailableCapacity: Int? {return (try? rootURL.resourceValues(forKeys: [.volumeAvailableCapacityKey]))?.volumeAvailableCapacity}/// The volume’s available capacity in bytes for storing important resources.@available(iOS 11.0, *)public static var volumeAvailableCapacityForImportantUsage: Int64? {return (try? rootURL.resourceValues(forKeys: [.volumeAvailableCapacityForImportantUsageKey]))?.volumeAvailableCapacityForImportantUsage}/// The volume’s available capacity in bytes for storing nonessential resources.@available(iOS 11.0, *)public static var volumeAvailableCapacityForOpportunisticUsage: Int64? { //swiftlint:disable:this identifier_namereturn (try? rootURL.resourceValues(forKeys: [.volumeAvailableCapacityForOpportunisticUsageKey]))?.volumeAvailableCapacityForOpportunisticUsage}/// All volumes capacity information in bytes.@available(iOS 11.0, *)public static var volumes: [URLResourceKey: Int64]? {do {let values = try rootURL.resourceValues(forKeys: [.volumeAvailableCapacityForImportantUsageKey,.volumeAvailableCapacityKey,.volumeAvailableCapacityForOpportunisticUsageKey,.volumeTotalCapacityKey])return values.allValues.mapValues {if let int = $0 as? Int64 {return int}if let int = $0 as? Int {return Int64(int)}return 0}} catch {return nil}}}#endif#if os(iOS)// MARK: Apple Pencilextension Device {/**This option set describes the current Apple Pencils- firstGeneration: 1st Generation Apple Pencil- secondGeneration: 2nd Generation Apple Pencil*/public struct ApplePencilSupport: OptionSet {public var rawValue: UIntpublic init(rawValue: UInt) {self.rawValue = rawValue}public static let firstGeneration = ApplePencilSupport(rawValue: 0x01)public static let secondGeneration = ApplePencilSupport(rawValue: 0x02)}/// All Apple Pencil Capable Devicespublic static var allApplePencilCapableDevices: [Device] {return [.iPad6, .iPadAir3, .iPad7, .iPad8, .iPad9, .iPadAir4, .iPadAir5, .iPadMini5, .iPadMini6, .iPadPro9Inch, .iPadPro12Inch, .iPadPro12Inch2, .iPadPro10Inch, .iPadPro11Inch, .iPadPro12Inch3, .iPadPro11Inch2, .iPadPro12Inch4, .iPadPro11Inch3, .iPadPro12Inch5]}/// Returns supported version of the Apple Pencilpublic var applePencilSupport: ApplePencilSupport {switch self {case .iPad6: return .firstGenerationcase .iPadAir3: return .firstGenerationcase .iPad7: return .firstGenerationcase .iPad8: return .firstGenerationcase .iPad9: return .firstGenerationcase .iPadMini5: return .firstGenerationcase .iPadPro9Inch: return .firstGenerationcase .iPadPro12Inch: return .firstGenerationcase .iPadPro12Inch2: return .firstGenerationcase .iPadPro10Inch: return .firstGenerationcase .iPadAir4: return .secondGenerationcase .iPadAir5: return .secondGenerationcase .iPadMini6: return .secondGenerationcase .iPadPro11Inch: return .secondGenerationcase .iPadPro12Inch3: return .secondGenerationcase .iPadPro11Inch2: return .secondGenerationcase .iPadPro12Inch4: return .secondGenerationcase .iPadPro11Inch3: return .secondGenerationcase .iPadPro12Inch5: return .secondGenerationcase .simulator(let model): return model.applePencilSupportdefault: return []}}}#endif#if os(iOS)// MARK: Camerasextension Device {public enum CameraType {@available(*, deprecated, renamed: "wide")case normalcase widecase telephotocase ultraWide}/// Returns an array of the types of cameras the device haspublic var cameras: [CameraType] {switch self {case .iPodTouch5: return [.wide]case .iPodTouch6: return [.wide]case .iPodTouch7: return [.wide]case .iPhone4: return [.wide]case .iPhone4s: return [.wide]case .iPhone5: return [.wide]case .iPhone5c: return [.wide]case .iPhone5s: return [.wide]case .iPhone6: return [.wide]case .iPhone6Plus: return [.wide]case .iPhone6s: return [.wide]case .iPhone6sPlus: return [.wide]case .iPhone7: return [.wide]case .iPhoneSE: return [.wide]case .iPhone8: return [.wide]case .iPhoneXR: return [.wide]case .iPhoneSE2: return [.wide]case .iPhoneSE3: return [.wide]case .iPad2: return [.wide]case .iPad3: return [.wide]case .iPad4: return [.wide]case .iPadAir: return [.wide]case .iPadAir2: return [.wide]case .iPad5: return [.wide]case .iPad6: return [.wide]case .iPadAir3: return [.wide]case .iPad7: return [.wide]case .iPad8: return [.wide]case .iPad9: return [.wide]case .iPadAir4: return [.wide]case .iPadAir5: return [.wide]case .iPadMini: return [.wide]case .iPadMini2: return [.wide]case .iPadMini3: return [.wide]case .iPadMini4: return [.wide]case .iPadMini5: return [.wide]case .iPadMini6: return [.wide]case .iPadPro9Inch: return [.wide]case .iPadPro12Inch: return [.wide]case .iPadPro12Inch2: return [.wide]case .iPadPro10Inch: return [.wide]case .iPadPro11Inch: return [.wide]case .iPadPro12Inch3: return [.wide]case .iPhone7Plus: return [.wide, .telephoto]case .iPhone8Plus: return [.wide, .telephoto]case .iPhoneX: return [.wide, .telephoto]case .iPhoneXS: return [.wide, .telephoto]case .iPhoneXSMax: return [.wide, .telephoto]case .iPhone11: return [.wide, .ultraWide]case .iPhone12: return [.wide, .ultraWide]case .iPhone12Mini: return [.wide, .ultraWide]case .iPhone13: return [.wide, .ultraWide]case .iPhone13Mini: return [.wide, .ultraWide]case .iPadPro11Inch2: return [.wide, .ultraWide]case .iPadPro12Inch4: return [.wide, .ultraWide]case .iPadPro11Inch3: return [.wide, .ultraWide]case .iPadPro12Inch5: return [.wide, .ultraWide]case .iPhone11Pro: return [.wide, .telephoto, .ultraWide]case .iPhone11ProMax: return [.wide, .telephoto, .ultraWide]case .iPhone12Pro: return [.wide, .telephoto, .ultraWide]case .iPhone12ProMax: return [.wide, .telephoto, .ultraWide]case .iPhone13Pro: return [.wide, .telephoto, .ultraWide]case .iPhone13ProMax: return [.wide, .telephoto, .ultraWide]default: return []}}/// All devices that feature a camerapublic 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, .iPad2, .iPad3, .iPad4, .iPadAir, .iPadAir2, .iPad5, .iPad6, .iPadAir3, .iPad7, .iPad8, .iPad9, .iPadAir4, .iPadAir5, .iPadMini, .iPadMini2, .iPadMini3, .iPadMini4, .iPadMini5, .iPadMini6, .iPadPro9Inch, .iPadPro12Inch, .iPadPro12Inch2, .iPadPro10Inch, .iPadPro11Inch, .iPadPro12Inch3, .iPadPro11Inch2, .iPadPro12Inch4, .iPadPro11Inch3, .iPadPro12Inch5]}/// All devices that feature a normal camera@available(*, deprecated, renamed: "allDevicesWithWideCamera")public static var allDevicesWithNormalCamera: [Device] {return Device.allDevicesWithWideCamera}/// All devices that feature a wide camerapublic 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, .iPad2, .iPad3, .iPad4, .iPadAir, .iPadAir2, .iPad5, .iPad6, .iPadAir3, .iPad7, .iPad8, .iPad9, .iPadAir4, .iPadAir5, .iPadMini, .iPadMini2, .iPadMini3, .iPadMini4, .iPadMini5, .iPadMini6, .iPadPro9Inch, .iPadPro12Inch, .iPadPro12Inch2, .iPadPro10Inch, .iPadPro11Inch, .iPadPro12Inch3, .iPadPro11Inch2, .iPadPro12Inch4, .iPadPro11Inch3, .iPadPro12Inch5]}/// All devices that feature a telephoto camerapublic static var allDevicesWithTelephotoCamera: [Device] {return [.iPhone7Plus, .iPhone8Plus, .iPhoneX, .iPhoneXS, .iPhoneXSMax, .iPhone11Pro, .iPhone11ProMax, .iPhone12Pro, .iPhone12ProMax, .iPhone13Pro, .iPhone13ProMax]}/// All devices that feature an ultra wide camerapublic static var allDevicesWithUltraWideCamera: [Device] {return [.iPhone11, .iPhone11Pro, .iPhone11ProMax, .iPhone12, .iPhone12Mini, .iPhone12Pro, .iPhone12ProMax, .iPhone13, .iPhone13Mini, .iPhone13Pro, .iPhone13ProMax, .iPadPro11Inch2, .iPadPro12Inch4, .iPadPro11Inch3, .iPadPro12Inch5]}/// Returns whether or not the current device has a camerapublic var hasCamera: Bool {return !self.cameras.isEmpty}/// Returns whether or not the current device has a normal camera@available(*, deprecated, renamed: "hasWideCamera")public var hasNormalCamera: Bool {return self.hasWideCamera}/// Returns whether or not the current device has a wide camerapublic var hasWideCamera: Bool {return self.cameras.contains(.wide)}/// Returns whether or not the current device has a telephoto camerapublic var hasTelephotoCamera: Bool {return self.cameras.contains(.telephoto)}/// Returns whether or not the current device has an ultra wide camerapublic var hasUltraWideCamera: Bool {return self.cameras.contains(.ultraWide)}}#endif