1 import ExpoModulesCore 2 import UIKit 3 import MachO 4 5 public class DeviceModule: Module { 6 public func definition() -> ModuleDefinition { 7 Name("ExpoDevice") 8 9 Constants([ 10 "isDevice": isDevice(), 11 "brand": "Apple", 12 "manufacturer": "Apple", 13 "modelId": UIDevice.modelIdentifier, 14 "modelName": UIDevice.DeviceMap.modelName, 15 "deviceYearClass": UIDevice.DeviceMap.deviceYearClass, 16 "totalMemory": ProcessInfo.processInfo.physicalMemory, 17 "osName": UIDevice.current.systemName, 18 "osVersion": UIDevice.current.systemVersion, 19 "osBuildId": osBuildId(), 20 "osInternalBuildId": osBuildId(), 21 "deviceName": UIDevice.current.name, 22 "deviceType": getDeviceType(), 23 "supportedCpuArchitectures": cpuArchitectures() 24 ]) 25 26 AsyncFunction("getDeviceTypeAsync") { () -> Int in 27 return getDeviceType() 28 } 29 30 AsyncFunction("getUptimeAsync") { () -> Double in 31 return ProcessInfo.processInfo.systemUptime * 1000 32 } 33 34 AsyncFunction("isRootedExperimentalAsync") { () -> Bool in 35 return UIDevice.current.isJailbroken 36 } 37 } 38 } 39 40 func getDeviceType() -> Int { 41 // if it's a macOS Catalyst app 42 if ProcessInfo.processInfo.isMacCatalystApp { 43 return DeviceType.desktop.rawValue 44 } 45 46 // if it's built for iPad running on a Mac 47 if #available(iOS 14.0, *) { 48 if ProcessInfo.processInfo.isiOSAppOnMac { 49 return DeviceType.desktop.rawValue 50 } 51 } 52 53 switch UIDevice.current.userInterfaceIdiom { 54 case UIUserInterfaceIdiom.phone: 55 return DeviceType.phone.rawValue 56 case UIUserInterfaceIdiom.pad: 57 return DeviceType.tablet.rawValue 58 case UIUserInterfaceIdiom.tv: 59 return DeviceType.tv.rawValue 60 default: 61 return DeviceType.unknown.rawValue 62 } 63 } 64 65 func isDevice() -> Bool { 66 #if targetEnvironment(simulator) 67 return false 68 #else 69 return true 70 #endif 71 } 72 73 func osBuildId() -> String? { 74 #if os(tvOS) 75 return nil 76 #else 77 // Credit: https://stackoverflow.com/a/65858410 78 var mib: [Int32] = [CTL_KERN, KERN_OSVERSION] 79 let namelen = UInt32(MemoryLayout.size(ofValue: mib) / MemoryLayout.size(ofValue: mib[0])) 80 var bufferSize = 0 81 82 // Get the size for the buffer 83 sysctl(&mib, namelen, nil, &bufferSize, nil, 0) 84 85 var buildBuffer = [UInt8](repeating: 0, count: bufferSize) 86 let result = sysctl(&mib, namelen, &buildBuffer, &bufferSize, nil, 0) 87 88 if result >= 0 && bufferSize > 0 { 89 return String(bytesNoCopy: &buildBuffer, length: bufferSize - 1, encoding: .utf8, freeWhenDone: false) 90 } 91 92 return nil 93 #endif 94 } 95 96 func cpuArchitectures() -> [String]? { 97 // Credit: https://stackoverflow.com/a/70134518 98 guard let archRaw = NXGetLocalArchInfo().pointee.name else { 99 return nil 100 } 101 return [String(cString: archRaw)] 102 } 103 104 enum DeviceType: Int { 105 case unknown = 0 106 case phone = 1 107 case tablet = 2 108 case desktop = 3 109 case tv = 4 110 } 111