feat: Better Native Module Error Detection (#1515)

* feat: Add more Error insights when the Camera Module cannot be found

* Assert JSI is available

* Update error description

* fix

* Update CameraError.ts
This commit is contained in:
Marc Rousavy 2023-03-13 14:21:08 +01:00 committed by GitHub
parent 622d3830f1
commit f791c6b4cd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 72 additions and 20 deletions

View File

@ -1,11 +1,13 @@
import React from 'react';
import { requireNativeComponent, NativeModules, NativeSyntheticEvent, findNodeHandle, NativeMethods, Platform } from 'react-native';
import { requireNativeComponent, NativeSyntheticEvent, findNodeHandle, NativeMethods, Platform } from 'react-native';
import type { VideoFileType } from '.';
import type { CameraDevice } from './CameraDevice';
import type { ErrorWithCause } from './CameraError';
import { CameraCaptureError, CameraRuntimeError, tryParseNativeCameraError, isErrorWithCause } from './CameraError';
import type { CameraProps } from './CameraProps';
import type { Frame } from './Frame';
import { assertFrameProcessorsAvailable } from './JSIHelper';
import { CameraModule } from './NativeCameraModule';
import type { PhotoFile, TakePhotoOptions } from './PhotoFile';
import type { Point } from './Point';
import type { TakeSnapshotOptions } from './Snapshot';
@ -30,11 +32,6 @@ type NativeCameraViewProps = Omit<CameraProps, 'device' | 'onInitialized' | 'onE
type RefType = React.Component<NativeCameraViewProps> & Readonly<NativeMethods>;
//#endregion
// NativeModules automatically resolves 'CameraView' to 'CameraViewModule'
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const CameraModule = NativeModules.CameraView;
if (CameraModule == null) console.error("Camera: Native Module 'CameraView' was null! Did you run pod install?");
//#region Camera Component
/**
* ### A powerful `<Camera>` component.
@ -419,25 +416,14 @@ export class Camera extends React.PureComponent<CameraProps> {
//#endregion
//#region Lifecycle
/** @internal */
private assertFrameProcessorsEnabled(): void {
// @ts-expect-error JSI functions aren't typed
if (global.setFrameProcessor == null || global.unsetFrameProcessor == null) {
throw new CameraRuntimeError(
'frame-processor/unavailable',
'Frame Processors are not enabled. See https://mrousavy.github.io/react-native-vision-camera/docs/guides/troubleshooting',
);
}
}
private setFrameProcessor(frameProcessor: (frame: Frame) => void): void {
this.assertFrameProcessorsEnabled();
assertFrameProcessorsAvailable();
// @ts-expect-error JSI functions aren't typed
global.setFrameProcessor(this.handle, frameProcessor);
}
private unsetFrameProcessor(): void {
this.assertFrameProcessorsEnabled();
assertFrameProcessorsAvailable();
// @ts-expect-error JSI functions aren't typed
global.unsetFrameProcessor(this.handle);
}

View File

@ -50,7 +50,7 @@ export type CaptureError =
| 'capture/photo-not-enabled'
| 'capture/aborted'
| 'capture/unknown';
export type SystemError = 'system/no-camera-manager' | 'system/view-not-found';
export type SystemError = 'system/camera-module-not-found' | 'system/no-camera-manager' | 'system/view-not-found';
export type UnknownError = 'unknown/unknown';
/**

View File

@ -1,6 +1,9 @@
import type { Frame, FrameInternal } from './Frame';
import { Camera } from './Camera';
import { Worklets } from 'react-native-worklets/src';
import { assertJSIAvailable } from './JSIHelper';
assertJSIAvailable();
// Install VisionCamera Frame Processor JSI Bindings and Plugins
Camera.installFrameProcessorBindings();

22
src/JSIHelper.ts Normal file
View File

@ -0,0 +1,22 @@
import { CameraRuntimeError } from './CameraError';
export function assertJSIAvailable(): void {
// Check if we are running on-device (JSI)
// @ts-expect-error JSI functions aren't typed
if (global.nativeCallSyncHook == null) {
throw new CameraRuntimeError(
'frame-processor/unavailable',
'Failed to initialize VisionCamera Frame Processors: React Native is not running on-device. Frame Processors can only be used when synchronous method invocations (JSI) are possible. If you are using a remote debugger (e.g. Chrome), switch to an on-device debugger (e.g. Flipper) instead.',
);
}
}
export function assertFrameProcessorsAvailable(): void {
// @ts-expect-error JSI functions aren't typed
if (global.setFrameProcessor == null || global.unsetFrameProcessor == null) {
throw new CameraRuntimeError(
'frame-processor/unavailable',
'Frame Processors are not enabled. See https://mrousavy.github.io/react-native-vision-camera/docs/guides/troubleshooting',
);
}
}

41
src/NativeCameraModule.ts Normal file
View File

@ -0,0 +1,41 @@
import { NativeModules, Platform } from 'react-native';
import { CameraRuntimeError } from './CameraError';
const supportedPlatforms = ['ios', 'android', 'macos'];
// NativeModules automatically resolves 'CameraView' to 'CameraViewModule'
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
export const CameraModule = NativeModules.CameraView;
if (CameraModule == null) {
if (!supportedPlatforms.includes(Platform.OS)) {
throw new CameraRuntimeError(
'system/camera-module-not-found',
`Failed to initialize VisionCamera: VisionCamera currently does not work on ${Platform.OS}.`,
);
}
let message = 'Failed to initialize VisionCamera: The native Camera Module (`NativeModules.CameraView`) could not be found.';
message += '\n* Make sure react-native-vision-camera is correctly autolinked (run `npx react-native config` to verify)';
if (Platform.OS === 'ios' || Platform.OS === 'macos') message += '\n* Make sure you ran `pod install` in the ios/ directory.';
if (Platform.OS === 'android') message += '\n* Make sure gradle is synced.';
// check if Expo
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const ExpoConstants = NativeModules.NativeUnimoduleProxy?.modulesConstants?.ExponentConstants;
if (ExpoConstants != null) {
if (ExpoConstants.appOwnership === 'expo') {
// We're running Expo Go
throw new CameraRuntimeError(
'system/camera-module-not-found',
`react-native-vision-camera is not supported in Expo Go! Use EAS/expo prebuild instead (\`expo run:${Platform.OS}\`). For more info, see https://docs.expo.dev/workflow/prebuild/.`,
);
} else {
// We're running Expo bare / standalone
message += '\n* Make sure you ran `expo prebuild`.';
}
}
message += '\n* Make sure you rebuilt the app.';
throw new CameraRuntimeError('system/camera-module-not-found', message);
}