import { requireOptionalNativeModule } from "expo-modules-core"; import type * as ImagePickerModule from "expo-image-picker"; export type ImagePickerApi = typeof ImagePickerModule; /** The native module expo-image-picker is a JS wrapper around. */ const NATIVE_MODULE = "ExponentImagePicker"; /** * Whether photo capture can work at all in this build. * * `requireOptionalNativeModule` is the non-throwing twin of the * `requireNativeModule` call that expo-image-picker makes as it loads: it * returns null instead of raising `Cannot find native module * 'ExponentImagePicker'`. Asking first means the error is never constructed, * never logged, and never has a chance to escape into a driver's face — which * beats importing the package and catching the throw, because a throw that * happens while a module is evaluating can surface in places a try/catch * around the import does not cover. * * expo-modules-core itself is part of every Expo binary, so importing it here * is safe on exactly the old builds this is guarding against. */ export const isImagePickerAvailable = (): boolean => requireOptionalNativeModule(NATIVE_MODULE) !== null; /** * Loads expo-image-picker, or returns null when this binary predates it. * * The package resolves its native counterpart at *import* time, so importing * it at the top of a screen doesn't fail politely at the camera button: it * fails while the route tree is being built, taking the whole app down — * riders included — on any build made before the package was added. Deferring * the require moves that failure to the one tap that needs it and makes it * recoverable. * * A null return means one thing only: the app needs rebuilding. Photo capture * genuinely requires the native module; nothing here can substitute for it. */ let cached: ImagePickerApi | null = null; export const loadImagePicker = (): ImagePickerApi | null => { if (cached) return cached; if (!isImagePickerAvailable()) return null; try { // Static string so Metro still bundles it — only the evaluation is // deferred, not the packaging. // eslint-disable-next-line @typescript-eslint/no-var-requires cached = require("expo-image-picker") as ImagePickerApi; return cached; } catch (error) { console.log("[IMAGE_PICKER_UNAVAILABLE]: ", error); return null; } };