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