1 import UIKit 2 3 struct ExpoDeviceType { 4 let modelName: String 5 let deviceYearClass: Int? 6 } 7 8 public extension UIDevice { 9 // Credit: https://stackoverflow.com/a/26962452 10 static let modelIdentifier: String = { 11 var systemInfo = utsname() 12 uname(&systemInfo) 13 let machineMirror = Mirror(reflecting: systemInfo.machine) 14 return machineMirror.children.reduce("") { identifier, element in 15 guard let value = element.value as? Int8, value != 0 else { 16 return identifier 17 } 18 return identifier + String(UnicodeScalar(UInt8(value))) 19 } 20 }() 21 22 // swiftlint:disable closure_body_length 23 static internal let DeviceMap: ExpoDeviceType = { mapToDevicenull24 func mapToDevice(identifier: String) -> ExpoDeviceType { 25 let currentYear = Calendar(identifier: .gregorian).dateComponents([.year], from: Date()).year 26 27 #if os(iOS) 28 switch identifier { 29 case "iPod7,1": 30 return ExpoDeviceType(modelName: "iPod touch (6th generation)", deviceYearClass: 2015) 31 case "iPod9,1": 32 return ExpoDeviceType(modelName: "iPod touch (7th generation)", deviceYearClass: 2019) 33 case "iPhone6,1", "iPhone6,2": 34 return ExpoDeviceType(modelName: "iPhone 5s", deviceYearClass: 2013) 35 case "iPhone7,1": 36 return ExpoDeviceType(modelName: "iPhone 6 Plus", deviceYearClass: 2014) 37 case "iPhone7,2": 38 return ExpoDeviceType(modelName: "iPhone 6", deviceYearClass: 2014) 39 case "iPhone8,1": 40 return ExpoDeviceType(modelName: "iPhone 6s", deviceYearClass: 2015) 41 case "iPhone8,2": 42 return ExpoDeviceType(modelName: "iPhone 6s Plus", deviceYearClass: 2015) 43 case "iPhone9,1", "iPhone9,3": 44 return ExpoDeviceType(modelName: "iPhone 7", deviceYearClass: 2016) 45 case "iPhone9,2", "iPhone9,4": 46 return ExpoDeviceType(modelName: "iPhone 7 Plus", deviceYearClass: 2016) 47 case "iPhone10,1", "iPhone10,4": 48 return ExpoDeviceType(modelName: "iPhone 8", deviceYearClass: 2017) 49 case "iPhone10,2", "iPhone10,5": 50 return ExpoDeviceType(modelName: "iPhone 8 Plus", deviceYearClass: 2017) 51 case "iPhone10,3", "iPhone10,6": 52 return ExpoDeviceType(modelName: "iPhone X", deviceYearClass: 2017) 53 case "iPhone11,2": 54 return ExpoDeviceType(modelName: "iPhone XS", deviceYearClass: 2018) 55 case "iPhone11,4", "iPhone11,6": 56 return ExpoDeviceType(modelName: "iPhone XS Max", deviceYearClass: 2018) 57 case "iPhone11,8": 58 return ExpoDeviceType(modelName: "iPhone XR", deviceYearClass: 2018) 59 case "iPhone12,1": 60 return ExpoDeviceType(modelName: "iPhone 11", deviceYearClass: 2019) 61 case "iPhone12,3": 62 return ExpoDeviceType(modelName: "iPhone 11 Pro", deviceYearClass: 2019) 63 case "iPhone12,5": 64 return ExpoDeviceType(modelName: "iPhone 11 Pro Max", deviceYearClass: 2019) 65 case "iPhone13,1": 66 return ExpoDeviceType(modelName: "iPhone 12 mini", deviceYearClass: 2020) 67 case "iPhone13,2": 68 return ExpoDeviceType(modelName: "iPhone 12", deviceYearClass: 2020) 69 case "iPhone13,3": 70 return ExpoDeviceType(modelName: "iPhone 12 Pro", deviceYearClass: 2020) 71 case "iPhone13,4": 72 return ExpoDeviceType(modelName: "iPhone 12 Pro Max", deviceYearClass: 2020) 73 case "iPhone14,4": 74 return ExpoDeviceType(modelName: "iPhone 13 mini", deviceYearClass: 2021) 75 case "iPhone14,5": 76 return ExpoDeviceType(modelName: "iPhone 13", deviceYearClass: 2021) 77 case "iPhone14,2": 78 return ExpoDeviceType(modelName: "iPhone 13 Pro", deviceYearClass: 2021) 79 case "iPhone14,3": 80 return ExpoDeviceType(modelName: "iPhone 13 Pro Max", deviceYearClass: 2021) 81 case "iPhone14,7": 82 return ExpoDeviceType(modelName: "iPhone 14", deviceYearClass: 2022) 83 case "iPhone14,8": 84 return ExpoDeviceType(modelName: "iPhone 14 Plus", deviceYearClass: 2022) 85 case "iPhone15,2": 86 return ExpoDeviceType(modelName: "iPhone 14 Pro", deviceYearClass: 2022) 87 case "iPhone15,3": 88 return ExpoDeviceType(modelName: "iPhone 14 Pro Max", deviceYearClass: 2022) 89 case "iPhone8,4": 90 return ExpoDeviceType(modelName: "iPhone SE", deviceYearClass: 2016) 91 case "iPhone12,8": 92 return ExpoDeviceType(modelName: "iPhone SE (2nd generation)", deviceYearClass: 2020) 93 case "iPhone14,6": 94 return ExpoDeviceType(modelName: "iPhone SE (3rd generation)", deviceYearClass: 2022) 95 case "iPad6,11", "iPad6,12": 96 return ExpoDeviceType(modelName: "iPad (5th generation)", deviceYearClass: 2017) 97 case "iPad7,5", "iPad7,6": 98 return ExpoDeviceType(modelName: "iPad (6th generation)", deviceYearClass: 2018) 99 case "iPad7,11", "iPad7,12": 100 return ExpoDeviceType(modelName: "iPad (7th generation)", deviceYearClass: 2019) 101 case "iPad11,6", "iPad11,7": 102 return ExpoDeviceType(modelName: "iPad (8th generation)", deviceYearClass: 2020) 103 case "iPad12,1", "iPad12,2": 104 return ExpoDeviceType(modelName: "iPad (9th generation)", deviceYearClass: 2021) 105 case "iPad4,1", "iPad4,2", "iPad4,3": 106 return ExpoDeviceType(modelName: "iPad Air", deviceYearClass: 2013) 107 case "iPad5,3", "iPad5,4": 108 return ExpoDeviceType(modelName: "iPad Air 2", deviceYearClass: 2014) 109 case "iPad11,3", "iPad11,4": 110 return ExpoDeviceType(modelName: "iPad Air (3rd generation)", deviceYearClass: 2019) 111 case "iPad13,1", "iPad13,2": 112 return ExpoDeviceType(modelName: "iPad Air (4th generation)", deviceYearClass: 2020) 113 case "iPad13,16", "iPad13,17": 114 return ExpoDeviceType(modelName: "iPad Air (5th generation)", deviceYearClass: 2022) 115 case "iPad4,4", "iPad4,5", "iPad4,6": 116 return ExpoDeviceType(modelName: "iPad mini 2", deviceYearClass: 2013) 117 case "iPad4,7", "iPad4,8", "iPad4,9": 118 return ExpoDeviceType(modelName: "iPad mini 3", deviceYearClass: 2014) 119 case "iPad5,1", "iPad5,2": 120 return ExpoDeviceType(modelName: "iPad mini 4", deviceYearClass: 2015) 121 case "iPad11,1", "iPad11,2": 122 return ExpoDeviceType(modelName: "iPad mini (5th generation)", deviceYearClass: 2019) 123 case "iPad14,1", "iPad14,2": 124 return ExpoDeviceType(modelName: "iPad mini (6th generation)", deviceYearClass: 2021) 125 case "iPad6,3", "iPad6,4": 126 return ExpoDeviceType(modelName: "iPad Pro (9.7-inch)", deviceYearClass: 2016) 127 case "iPad7,3", "iPad7,4": 128 return ExpoDeviceType(modelName: "iPad Pro (10.5-inch)", deviceYearClass: 2017) 129 case "iPad8,1", "iPad8,2", "iPad8,3", "iPad8,4": 130 return ExpoDeviceType(modelName: "iPad Pro (11-inch) (1st generation)", deviceYearClass: 2018) 131 case "iPad8,9", "iPad8,10": 132 return ExpoDeviceType(modelName: "iPad Pro (11-inch) (2nd generation)", deviceYearClass: 2020) 133 case "iPad13,4", "iPad13,5", "iPad13,6", "iPad13,7": 134 return ExpoDeviceType(modelName: "iPad Pro (11-inch) (3rd generation)", deviceYearClass: 2021) 135 case "iPad6,7", "iPad6,8": 136 return ExpoDeviceType(modelName: "iPad Pro (12.9-inch) (1st generation)", deviceYearClass: 2015) 137 case "iPad7,1", "iPad7,2": 138 return ExpoDeviceType(modelName: "iPad Pro (12.9-inch) (2nd generation)", deviceYearClass: 2017) 139 case "iPad8,5", "iPad8,6", "iPad8,7", "iPad8,8": 140 return ExpoDeviceType(modelName: "iPad Pro (12.9-inch) (3rd generation)", deviceYearClass: 2018) 141 case "iPad8,11", "iPad8,12": 142 return ExpoDeviceType(modelName: "iPad Pro (12.9-inch) (4th generation)", deviceYearClass: 2020) 143 case "iPad13,8", "iPad13,9", "iPad13,10", "iPad13,11": 144 return ExpoDeviceType(modelName: "iPad Pro (12.9-inch) (5th generation)", deviceYearClass: 2021) 145 case "AppleTV5,3": 146 return ExpoDeviceType(modelName: "Apple TV HD (4th Generation, Siri)", deviceYearClass: 2015) 147 case "AppleTV6,2": 148 return ExpoDeviceType(modelName: "Apple TV 4K", deviceYearClass: 2017) 149 case "i386", "x86_64", "arm64": 150 return ExpoDeviceType(modelName: "Simulator iOS", deviceYearClass: currentYear) 151 default: 152 return ExpoDeviceType(modelName: identifier, deviceYearClass: currentYear) 153 } 154 #elseif os(tvOS) 155 switch identifier { 156 case "AppleTV5,3": 157 return ExpoDeviceType(modelName: "Apple TV HD (4th Generation, Siri)", deviceYearClass: 2015) 158 case "AppleTV6,2": 159 return ExpoDeviceType(modelName: "Apple TV 4K", deviceYearClass: 2017) 160 case "i386", "x86_64": 161 return ExpoDeviceType(modelName: "Simulator tvOS", deviceYearClass: currentYear) 162 default: 163 return identifier 164 } 165 #endif 166 } 167 168 return mapToDevice(identifier: modelIdentifier) 169 }() 170 // swiftlint:enable closure_body_length 171 172 // Credit: https://github.com/developerinsider/isJailBroken/blob/master/IsJailBroken/Extension/UIDevice%2BJailBroken.swift 173 var isSimulator: Bool { 174 return TARGET_OS_SIMULATOR != 0 175 } 176 177 var isJailbroken: Bool { 178 if UIDevice.current.isSimulator { 179 return false 180 } 181 182 return JailbreakHelper.hasCydiaInstalled() || JailbreakHelper.doesContainSuspiciousApps() || 183 JailbreakHelper.doesSuspiciousSystemPathExist() || JailbreakHelper.canEditSystemFiles() 184 } 185 } 186 187 // Credit: https://github.com/developerinsider/isJailBroken/blob/master/IsJailBroken/Extension/UIDevice%2BJailBroken.swift 188 private struct JailbreakHelper { hasCydiaInstallednull189 static func hasCydiaInstalled() -> Bool { 190 if let url = URL(string: "cydia://") { 191 return UIApplication.shared.canOpenURL(url) 192 } 193 return false 194 } 195 doesContainSuspiciousAppsnull196 static func doesContainSuspiciousApps() -> Bool { 197 for path in suspiciousAppsPathToCheck where FileManager.default.fileExists(atPath: path) { 198 return true 199 } 200 return false 201 } 202 doesSuspiciousSystemPathExistnull203 static func doesSuspiciousSystemPathExist() -> Bool { 204 for path in suspiciousSystemPathsToCheck where FileManager.default.fileExists(atPath: path) { 205 return true 206 } 207 return false 208 } 209 canEditSystemFilesnull210 static func canEditSystemFiles() -> Bool { 211 let jailbreakText = "Developer Insider" 212 do { 213 try jailbreakText.write(toFile: jailbreakText, atomically: true, encoding: .utf8) 214 return true 215 } catch { 216 return false 217 } 218 } 219 220 /** 221 Add more paths here to check for jail break 222 */ 223 static var suspiciousAppsPathToCheck: [String] { 224 return [ 225 "/Applications/Cydia.app", 226 "/Applications/blackra1n.app", 227 "/Applications/FakeCarrier.app", 228 "/Applications/Icy.app", 229 "/Applications/IntelliScreen.app", 230 "/Applications/MxTube.app", 231 "/Applications/RockApp.app", 232 "/Applications/SBSettings.app", 233 "/Applications/WinterBoard.app" 234 ] 235 } 236 237 static var suspiciousSystemPathsToCheck: [String] { 238 return [ 239 "/Library/MobileSubstrate/DynamicLibraries/LiveClock.plist", 240 "/Library/MobileSubstrate/DynamicLibraries/Veency.plist", 241 "/private/var/lib/apt", 242 "/private/var/lib/apt/", 243 "/private/var/lib/cydia", 244 "/private/var/mobile/Library/SBSettings/Themes", 245 "/private/var/stash", 246 "/private/var/tmp/cydia.log", 247 "/System/Library/LaunchDaemons/com.ikey.bbot.plist", 248 "/System/Library/LaunchDaemons/com.saurik.Cydia.Startup.plist", 249 "/usr/bin/sshd", 250 "/usr/libexec/sftp-server", 251 "/usr/sbin/sshd", 252 "/etc/apt", 253 "/bin/bash", 254 "/Library/MobileSubstrate/MobileSubstrate.dylib" 255 ] 256 } 257 } 258