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:
parent
622d3830f1
commit
f791c6b4cd
@ -1,11 +1,13 @@
|
|||||||
import React from 'react';
|
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 { VideoFileType } from '.';
|
||||||
import type { CameraDevice } from './CameraDevice';
|
import type { CameraDevice } from './CameraDevice';
|
||||||
import type { ErrorWithCause } from './CameraError';
|
import type { ErrorWithCause } from './CameraError';
|
||||||
import { CameraCaptureError, CameraRuntimeError, tryParseNativeCameraError, isErrorWithCause } from './CameraError';
|
import { CameraCaptureError, CameraRuntimeError, tryParseNativeCameraError, isErrorWithCause } from './CameraError';
|
||||||
import type { CameraProps } from './CameraProps';
|
import type { CameraProps } from './CameraProps';
|
||||||
import type { Frame } from './Frame';
|
import type { Frame } from './Frame';
|
||||||
|
import { assertFrameProcessorsAvailable } from './JSIHelper';
|
||||||
|
import { CameraModule } from './NativeCameraModule';
|
||||||
import type { PhotoFile, TakePhotoOptions } from './PhotoFile';
|
import type { PhotoFile, TakePhotoOptions } from './PhotoFile';
|
||||||
import type { Point } from './Point';
|
import type { Point } from './Point';
|
||||||
import type { TakeSnapshotOptions } from './Snapshot';
|
import type { TakeSnapshotOptions } from './Snapshot';
|
||||||
@ -30,11 +32,6 @@ type NativeCameraViewProps = Omit<CameraProps, 'device' | 'onInitialized' | 'onE
|
|||||||
type RefType = React.Component<NativeCameraViewProps> & Readonly<NativeMethods>;
|
type RefType = React.Component<NativeCameraViewProps> & Readonly<NativeMethods>;
|
||||||
//#endregion
|
//#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
|
//#region Camera Component
|
||||||
/**
|
/**
|
||||||
* ### A powerful `<Camera>` component.
|
* ### A powerful `<Camera>` component.
|
||||||
@ -419,25 +416,14 @@ export class Camera extends React.PureComponent<CameraProps> {
|
|||||||
//#endregion
|
//#endregion
|
||||||
|
|
||||||
//#region Lifecycle
|
//#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 {
|
private setFrameProcessor(frameProcessor: (frame: Frame) => void): void {
|
||||||
this.assertFrameProcessorsEnabled();
|
assertFrameProcessorsAvailable();
|
||||||
// @ts-expect-error JSI functions aren't typed
|
// @ts-expect-error JSI functions aren't typed
|
||||||
global.setFrameProcessor(this.handle, frameProcessor);
|
global.setFrameProcessor(this.handle, frameProcessor);
|
||||||
}
|
}
|
||||||
|
|
||||||
private unsetFrameProcessor(): void {
|
private unsetFrameProcessor(): void {
|
||||||
this.assertFrameProcessorsEnabled();
|
assertFrameProcessorsAvailable();
|
||||||
// @ts-expect-error JSI functions aren't typed
|
// @ts-expect-error JSI functions aren't typed
|
||||||
global.unsetFrameProcessor(this.handle);
|
global.unsetFrameProcessor(this.handle);
|
||||||
}
|
}
|
||||||
|
@ -50,7 +50,7 @@ export type CaptureError =
|
|||||||
| 'capture/photo-not-enabled'
|
| 'capture/photo-not-enabled'
|
||||||
| 'capture/aborted'
|
| 'capture/aborted'
|
||||||
| 'capture/unknown';
|
| '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';
|
export type UnknownError = 'unknown/unknown';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
import type { Frame, FrameInternal } from './Frame';
|
import type { Frame, FrameInternal } from './Frame';
|
||||||
import { Camera } from './Camera';
|
import { Camera } from './Camera';
|
||||||
import { Worklets } from 'react-native-worklets/src';
|
import { Worklets } from 'react-native-worklets/src';
|
||||||
|
import { assertJSIAvailable } from './JSIHelper';
|
||||||
|
|
||||||
|
assertJSIAvailable();
|
||||||
|
|
||||||
// Install VisionCamera Frame Processor JSI Bindings and Plugins
|
// Install VisionCamera Frame Processor JSI Bindings and Plugins
|
||||||
Camera.installFrameProcessorBindings();
|
Camera.installFrameProcessorBindings();
|
||||||
|
22
src/JSIHelper.ts
Normal file
22
src/JSIHelper.ts
Normal 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
41
src/NativeCameraModule.ts
Normal 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);
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user