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 { 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);
|
||||
}
|
||||
|
@ -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';
|
||||
|
||||
/**
|
||||
|
@ -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
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