11import { NitroModules } from 'react-native-nitro-modules' ;
2- import { getHostComponent , type ViewConfig } from 'react-native-nitro-modules' ;
3- import type {
4- BiometricPromptMethods ,
5- BiometricPromptProps ,
6- } from './BiometricPromptView.nitro' ;
2+ // (Hybrid View host component intentionally omitted for stability)
73import type {
84 SensitiveInfo ,
95 StorageOptions ,
@@ -13,8 +9,38 @@ import type {
139} from './SensitiveInfo.nitro' ;
1410import { withBiometrics , withStrongBox } from './SensitiveInfo.nitro' ;
1511
16- const SensitiveInfoHybridObject =
17- NitroModules . createHybridObject < SensitiveInfo > ( 'SensitiveInfo' ) ;
12+ let _SensitiveInfoHybridObject : SensitiveInfo | null = null ;
13+ let _initAttempted = false ;
14+
15+ async function waitForHybrid < T > ( create : ( ) => T ) : Promise < T > {
16+ // If we already tried, just create (it should be ready by now or throw clearly)
17+ if ( _initAttempted ) return create ( ) ;
18+ _initAttempted = true ;
19+
20+ // Small retry loop: give Nitro a moment to register on app start
21+ const maxTries = 8 ;
22+ const backoffMs = [ 0 , 10 , 20 , 40 , 80 , 120 , 180 , 250 ] ;
23+
24+ // Native init now happens in the Package static init (Android) and ObjC/Swift load (iOS)
25+ for ( let i = 0 ; i < maxTries ; i ++ ) {
26+ try {
27+ return create ( ) ;
28+ } catch ( e ) {
29+ await new Promise ( ( r ) => setTimeout ( r , backoffMs [ i ] ?? 200 ) ) ;
30+ }
31+ }
32+ // Final attempt (will throw with the helpful Nitro error message if still not registered)
33+ return create ( ) ;
34+ }
35+
36+ async function getSensitiveInfoAsync ( ) : Promise < SensitiveInfo > {
37+ if ( _SensitiveInfoHybridObject ) return _SensitiveInfoHybridObject ;
38+ const instance = await waitForHybrid ( ( ) =>
39+ NitroModules . createHybridObject < SensitiveInfo > ( 'SensitiveInfo' )
40+ ) ;
41+ _SensitiveInfoHybridObject = instance ;
42+ return instance ;
43+ }
1844
1945/**
2046 * Get a stored value by key.
@@ -36,7 +62,7 @@ export function getItem(
3662 key : string ,
3763 options ?: StorageOptions
3864) : Promise < string | null > {
39- return SensitiveInfoHybridObject . getItem ( key , options ) ;
65+ return getSensitiveInfoAsync ( ) . then ( ( m ) => m . getItem ( key , options ) ) ;
4066}
4167
4268/**
@@ -63,7 +89,7 @@ export function setItem(
6389 value : string ,
6490 options ?: StorageOptions
6591) : Promise < void > {
66- return SensitiveInfoHybridObject . setItem ( key , value , options ) ;
92+ return getSensitiveInfoAsync ( ) . then ( ( m ) => m . setItem ( key , value , options ) ) ;
6793}
6894
6995/**
@@ -86,7 +112,7 @@ export function removeItem(
86112 key : string ,
87113 options ?: StorageOptions
88114) : Promise < void > {
89- return SensitiveInfoHybridObject . removeItem ( key , options ) ;
115+ return getSensitiveInfoAsync ( ) . then ( ( m ) => m . removeItem ( key , options ) ) ;
90116}
91117
92118/**
@@ -105,7 +131,7 @@ export function getAllItems(
105131export function getAllItems (
106132 options ?: StorageOptions
107133) : Promise < Record < string , string > > {
108- return SensitiveInfoHybridObject . getAllItems ( options ) ;
134+ return getSensitiveInfoAsync ( ) . then ( ( m ) => m . getAllItems ( options ) ) ;
109135}
110136
111137/**
@@ -116,21 +142,21 @@ export function clear(options: StandardOrStrongBoxOptions): Promise<void>;
116142export function clear ( options : BiometricStorageOptions ) : Promise < void > ;
117143export function clear ( options ?: StorageOptions ) : Promise < void > ;
118144export function clear ( options ?: StorageOptions ) : Promise < void > {
119- return SensitiveInfoHybridObject . clear ( options ) ;
145+ return getSensitiveInfoAsync ( ) . then ( ( m ) => m . clear ( options ) ) ;
120146}
121147
122148/**
123149 * Check if biometric authentication is available on the device.
124150 */
125151export function isBiometricAvailable ( ) : Promise < boolean > {
126- return SensitiveInfoHybridObject . isBiometricAvailable ( ) ;
152+ return getSensitiveInfoAsync ( ) . then ( ( m ) => m . isBiometricAvailable ( ) ) ;
127153}
128154
129155/**
130156 * Check if StrongBox is available on the device.
131157 */
132158export function isStrongBoxAvailable ( ) : Promise < boolean > {
133- return SensitiveInfoHybridObject . isStrongBoxAvailable ( ) ;
159+ return getSensitiveInfoAsync ( ) . then ( ( m ) => m . isStrongBoxAvailable ( ) ) ;
134160}
135161
136162/**
@@ -173,32 +199,10 @@ export type {
173199} from './hooks/useSensitiveInfo' ;
174200export { BiometricAuthenticator } from './utils/BiometricAuthenticator' ;
175201
176- // Hybrid View: BiometricPromptView
177- let _BiometricPromptNativeView : any ;
178- try {
179- // Provide a minimal ViewConfig. Nitro uses uiViewClassName for lookup,
180- // and validAttributes to pass props to the native Hybrid View.
181- const viewConfig : ViewConfig < BiometricPromptProps > = {
182- uiViewClassName : 'BiometricPromptView' ,
183- bubblingEventTypes : { } ,
184- directEventTypes : { } ,
185- validAttributes : {
186- promptTitle : true ,
187- promptSubtitle : true ,
188- promptDescription : true ,
189- cancelButtonText : true ,
190- allowDeviceCredential : true ,
191- } ,
192- } as const ;
193- _BiometricPromptNativeView = getHostComponent <
194- BiometricPromptProps ,
195- BiometricPromptMethods
196- > ( 'BiometricPromptView' , ( ) => viewConfig ) ;
197- } catch {
198- // During tests or when nitrogen hasn't run yet.
199- }
200-
201- export const BiometricPromptView = _BiometricPromptNativeView as any ;
202+ // Note: We avoid eagerly registering the HybridView host component to prevent
203+ // Fabric crashes on some setups. If needed in the future, we can expose a
204+ // helper that lazily calls getHostComponent.
205+ export const BiometricPromptView = undefined as unknown as any ;
202206
203207// Export types
204208export type {
0 commit comments