React Native: How can I detect if my code is running in the Simulator?
AndroidIosReact NativeAndroid Problem Overview
In a Obj-C iOS app I can use #if (TARGET_IPHONE_SIMULATOR)
to write simulator-only code.
In react native I can use:
if (__DEV__) {
.. do something special
}
.. to detect development mode.
We can use Platform.OS === 'ios'
to detect the platform (Android/iOS).
See here for more info Platform Docs
But how do we detect if the app is running in the simulator?
The reason I ask is that my app uses the camera to scan barcodes, and this isn't supported in the iOS Simulator.
Android Solutions
Solution 1 - Android
You can do this pretty easily with react-native-device-info, like so:
import DeviceInfo from 'react-native-device-info'
isSimulator() {
// https://github.com/react-native-community/react-native-device-info#isemulator
return DeviceInfo.isEmulator();
},
Solution 2 - Android
The easiest solution I can think of, which does not require creating a native module (or modifying an existing one), would be to pass this parameter as a react component property.
In your AppDelegate
where the RCTRootView
is initialized, you check if it's the simulator as you would do in a regular iOS app; you then pass this info to the react root-view as its initialProperties
:
BOOL isSimulator = NO;
#if TARGET_IPHONE_SIMULATOR
isSimulator = YES;
#endif
RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation
moduleName:@"ReactDemo"
initialProperties:@{@"isSimulator": @(isSimulator)}
launchOptions:launchOptions];
Now you can access it in the JavaScript via the props of your react component:
this.props.isSimulator
On Android, in you MainActivity
which extends ReactActivity
you can use a similar approach:
import android.os.Build;
import android.os.Bundle;
public boolean isEmulator() {
return Build.FINGERPRINT.startsWith("generic")
|| Build.FINGERPRINT.startsWith("unknown")
|| Build.MODEL.contains("google_sdk")
|| Build.MODEL.contains("Emulator")
|| Build.MODEL.contains("Android SDK built for x86")
|| Build.MANUFACTURER.contains("Genymotion")
|| (Build.BRAND.startsWith("generic") && Build.DEVICE.startsWith("generic"))
|| "google_sdk".equals(Build.PRODUCT);
}
@Override
protected Bundle getLaunchOptions() {
Bundle opts = new Bundle();
opts.putBoolean("isEmulator", isEmulator());
return opts;
}
Solution 3 - Android
If you're building a CRNA/Expo app you can use Device.isDevice
https://docs.expo.dev/versions/latest/sdk/device/#deviceisdevice
import * as Device from 'expo-device';
//....
console.log(Device.isDevice) // => false if simulator
Solution 4 - Android
Using react-native-device-info you can get the following data (executed on a simulator):
getUniqueID: DB71DCB5-6BB0-497B-BE9E-A02BCC1235B7
getInstanceID: undefined
getDeviceId: x86_64
getManufacturer: Apple
getModel: Simulator
getBrand: Apple
getSystemName: iOS
getSystemVersion: 10.1
getBundleId: org.reactjs.native.example.project
getBuildNumber: 1
getVersion: 1.0
getReadableVersion: 1.0.1
getDeviceName:MacBook Pro
getUserAgent: Mozilla/5.0 (iPhone; CPU iPhone OS 10_1 like Mac OS X) AppleWebKit/602.2.14 (KHTML, like Gecko) Mobile/14B72
getDeviceLocale: en
getDeviceCountry: US
getTimezone: America/Panama
isEmulator: true
isTablet: false
Solution 5 - Android
Currently, there isn't any way to see if you are running from a simulator in JS.
I would suggest to add the conditional TARGET_IPHONE_SIMULATOR
to check in your native code (if you wrote your own module). Or perhaps use a 3rd party module that doesn't render the camera if in a simulator...ie: react-native-camera: https://github.com/lwansbrough/react-native-camera/search?utf8=%E2%9C%93&q=TARGET_IPHONE_SIMULATOR
Solution 6 - Android
import getHostForRN from 'rn-host-detect';
const IS_SIMULATOR = getHostForRN('127.0.0.1') == "localhost";
This works to differentiate my iOS simulator from my actual device because the simulator returns localhost
and the iOS device returns 127.0.0.1
. Have not tested it on Android but let me know if it helps you guys.