Skip to content

Commit 4b2febf

Browse files
committed
refactor: Simplify SensitiveInfoPackage and improve hybrid object initialization
1 parent f554332 commit 4b2febf

3 files changed

Lines changed: 58 additions & 72 deletions

File tree

Lines changed: 10 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,23 @@
11
package com.margelo.nitro.sensitiveinfo
22

3-
import com.facebook.react.TurboReactPackage
3+
import androidx.annotation.NonNull
4+
import com.facebook.react.ReactPackage
45
import com.facebook.react.bridge.NativeModule
56
import com.facebook.react.bridge.ReactApplicationContext
6-
import com.facebook.react.module.model.ReactModuleInfoProvider
77
import com.facebook.react.uimanager.ViewManager
8-
import androidx.annotation.NonNull
9-
10-
class SensitiveInfoPackage : TurboReactPackage() {
11-
override fun getModule(name: String, reactContext: ReactApplicationContext): NativeModule? = when (name) {
12-
"AndroidBiometric" -> AndroidBiometricModule(reactContext)
13-
else -> null
14-
}
158

16-
override fun getReactModuleInfoProvider(): ReactModuleInfoProvider {
17-
return ReactModuleInfoProvider { HashMap() }
18-
}
9+
class SensitiveInfoPackage : ReactPackage {
10+
override fun createNativeModules(reactContext: ReactApplicationContext): List<NativeModule> = listOf(
11+
AndroidBiometricModule(reactContext)
12+
)
1913

20-
override fun createViewManagers(@NonNull reactContext: ReactApplicationContext): MutableList<ViewManager<*, *>> {
21-
val list = mutableListOf<ViewManager<*, *>>()
22-
list.add(HybridBiometricPromptViewManager.create())
23-
return list
24-
}
14+
override fun createViewManagers(@NonNull reactContext: ReactApplicationContext): List<ViewManager<*, *>> = listOf(
15+
HybridBiometricPromptViewManager.create()
16+
)
2517

2618
companion object {
2719
init {
28-
System.loadLibrary("sensitiveinfo")
20+
sensitiveinfoOnLoad.initializeNative()
2921
}
3022
}
3123
}

react-native.config.js

Lines changed: 4 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,8 @@
1-
const path = require('path');
2-
31
module.exports = {
4-
dependencies: {
5-
// Ensure the core Nitro modules package is autolinked
6-
'react-native-nitro-modules': {
7-
root: path.resolve(
8-
__dirname,
9-
'node_modules',
10-
'react-native-nitro-modules'
11-
),
12-
platforms: {
13-
ios: {},
14-
android: {},
15-
},
2+
dependency: {
3+
platforms: {
4+
ios: {},
5+
android: {},
166
},
177
},
188
};

src/index.tsx

Lines changed: 44 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,5 @@
11
import { 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)
73
import type {
84
SensitiveInfo,
95
StorageOptions,
@@ -13,8 +9,38 @@ import type {
139
} from './SensitiveInfo.nitro';
1410
import { 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(
105131
export 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>;
116142
export function clear(options: BiometricStorageOptions): Promise<void>;
117143
export function clear(options?: StorageOptions): Promise<void>;
118144
export 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
*/
125151
export 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
*/
132158
export 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';
174200
export { 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
204208
export type {

0 commit comments

Comments
 (0)