fix(rebreak-native): don't crash on old dev-clients missing ExpoLocalAuthentication

appLock.ts is imported from the root layout, so an unguarded top-level
`import * as LocalAuthentication from 'expo-local-authentication'` crashes the
whole app at launch on a dev-client built before the dependency was added
("Cannot find native module 'ExpoLocalAuthentication'"). Load it via a guarded
require; if absent → app lock reports `available: false`, everything else runs.
Real builds (EAS / fresh prebuild) ship the module and work normally.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
chahinebrini 2026-05-12 19:57:53 +02:00
parent aa9466aa92
commit 4a17c7942d

View File

@ -1,6 +1,21 @@
import { create } from 'zustand';
import AsyncStorage from '@react-native-async-storage/async-storage';
import * as LocalAuthentication from 'expo-local-authentication';
// expo-local-authentication ist ein NATIVE-Modul. In einem Dev-Client, der noch
// VOR dem Hinzufügen dieser Dependency gebaut wurde, fehlt der native Teil →
// `require()` wirft "Cannot find native module 'ExpoLocalAuthentication'" und
// würde die ganze App beim Start crashen (appLock wird im RootLayout importiert).
// Daher: defensiv laden. Fehlt das Modul → App-Sperre einfach „nicht verfügbar",
// der Rest der App läuft normal weiter. In jedem echten Build (EAS / frischer
// `expo prebuild`) ist das Modul drin und alles funktioniert.
type LocalAuthModule = typeof import('expo-local-authentication');
let LocalAuthentication: LocalAuthModule | null = null;
try {
// eslint-disable-next-line @typescript-eslint/no-var-requires, global-require
LocalAuthentication = require('expo-local-authentication') as LocalAuthModule;
} catch {
LocalAuthentication = null;
}
/**
* App-Sperre (Face ID / Touch ID).
@ -47,6 +62,11 @@ export const useAppLockStore = create<AppLockState>((set, get) => ({
ready: false,
init: async () => {
if (!LocalAuthentication) {
// Native-Modul fehlt (alter Dev-Client) → Sperre nicht verfügbar, App läuft weiter.
set({ enabled: false, available: false, locked: false, ready: true });
return;
}
const [storedRaw, hasHardware, isEnrolled] = await Promise.all([
AsyncStorage.getItem(STORAGE_KEY),
LocalAuthentication.hasHardwareAsync(),
@ -76,6 +96,7 @@ export const useAppLockStore = create<AppLockState>((set, get) => ({
},
authenticate: async (promptMessage) => {
if (!LocalAuthentication) return false;
const result = await LocalAuthentication.authenticateAsync({
promptMessage: promptMessage ?? DEFAULT_PROMPT,
// Geräte-Passcode als Fallback erlauben (Face ID schlägt 3x fehl → Passcode).