1 import ExpoModulesCore
2 import UIKit
3 import MachO
4 
5 public class DeviceModule: Module {
definitionnull6   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 
getDeviceTypenull40 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 
isDevicenull65 func isDevice() -> Bool {
66   #if targetEnvironment(simulator)
67   return false
68   #else
69   return true
70   #endif
71 }
72 
osBuildIdnull73 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 
cpuArchitecturesnull96 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