Expo Adapters
Adapter table
| Subpath | Port | Expo Module | Factory |
|---|---|---|---|
./expo-biometrics | BiometricProvider | expo-local-authentication | createExpoBiometrics |
./expo-camera | CameraProvider | expo-camera | createExpoCamera |
./expo-secure-store | SecureStorageProvider | expo-secure-store | createExpoSecureStore |
./expo-haptics | HapticsProvider | expo-haptics | createExpoHaptics |
./expo-notifications | NotificationProvider | expo-notifications | createExpoNotifications |
./expo-purchases | PurchaseProvider | react-native-purchases | createExpoPurchases |
./expo-permissions | PermissionsProvider | expo-modules-core | createExpoPermissions |
./expo-sharing | ShareProvider | expo-sharing | createExpoSharing |
./expo-clipboard | ClipboardProvider | expo-clipboard | createExpoClipboard |
./expo-location | LocationProvider | expo-location | createExpoLocation |
./expo-media-library | MediaLibraryProvider | expo-media-library | createExpoMediaLibrary |
./expo-map | MapProvider | react-native-maps | createExpoMap |
./expo-web-browser | WebBrowserProvider | expo-web-browser | createExpoWebBrowser |
./expo-contacts | ContactsProvider | expo-contacts | createExpoContacts |
./expo-review | ReviewProvider | expo-store-review | createExpoReview |
./expo-keychain | KeychainProvider | expo-secure-store | createExpoKeychain |
./expo-video | VideoPlayer (render-prop) | expo-av | createExpoVideoPlayer |
./expo-qr-scanner | QRScanner (render-prop) | expo-camera | createExpoQRScanner |
./expo-web-view | WebView (render-prop) | react-native-webview | createExpoWebView |
Installation
Install only the Expo modules you actually use. Each adapter subpath is independent.
npx expo install expo-camera expo-local-authentication expo-haptics
Then import the factory from its subpath:
import { createExpoCamera } from '@objectifthunes/limestone-sdk/expo-camera';
import { createExpoBiometrics } from '@objectifthunes/limestone-sdk/expo-biometrics';
import { createExpoHaptics } from '@objectifthunes/limestone-sdk/expo-haptics';
Wiring adapters in limestone.config.ts
import { defineConfig } from '@objectifthunes/limestone-sdk';
import { createExpoBiometrics } from '@objectifthunes/limestone-sdk/expo-biometrics';
import { createExpoCamera } from '@objectifthunes/limestone-sdk/expo-camera';
import { createExpoNotifications } from '@objectifthunes/limestone-sdk/expo-notifications';
import { createExpoHaptics } from '@objectifthunes/limestone-sdk/expo-haptics';
const config = defineConfig({
theme: myTheme,
api: {
baseUrl: 'https://api.example.com',
auth: {
refreshEndpoint: '/auth/refresh',
onUnauthorized: () => router.push('/login'),
},
},
adapters: {
biometrics: createExpoBiometrics(),
camera: createExpoCamera(),
notifications: createExpoNotifications(),
haptics: createExpoHaptics(),
},
});
export default function App() {
return (
<LimestoneProvider config={config}>
<RootNavigator />
</LimestoneProvider>
);
}
Register only the adapters your app uses. Omitting an adapter from defineConfig simply means the corresponding hook or provider is never initialized.
Dev adapter
The ./dev subpath exports the same factory names as the individual Expo subpaths, but every factory is backed by an in-memory implementation. No native modules are compiled. No device hardware is accessed. Use it in Expo Go, simulators, and CI.
import {
createExpoBiometrics,
createExpoCamera,
createExpoSecureStore,
createExpoHaptics,
createExpoNotifications,
createExpoPurchases,
createExpoPermissions,
createExpoSharing,
createExpoClipboard,
createExpoLocation,
createExpoMediaLibrary,
createExpoMap,
createExpoWebBrowser,
createExpoContacts,
createExpoReview,
createExpoKeychain,
createExpoVideoPlayer,
createExpoQRScanner,
createExpoWebView,
} from '@objectifthunes/limestone-sdk/dev';
The factory names are identical to the production subpaths. Your defineConfig call does not change at all.
Testing doubles
The ./testing subpath exports createInMemory* factories. Each double exposes a state object for direct inspection and mutation in tests — no mocking framework required.
import {
createInMemoryBiometrics,
createInMemoryCamera,
createInMemorySecureStorage,
createInMemoryHaptics,
createInMemoryNotifications,
createInMemoryPurchases,
createInMemoryPermissions,
createInMemoryShare,
createInMemoryClipboard,
createInMemoryKeychain,
createInMemoryLocation,
createInMemoryMediaLibrary,
createInMemoryMap,
createInMemoryWebBrowser,
createInMemoryContacts,
createInMemoryReview,
createInMemoryVideoPlayer,
createInMemoryQRScanner,
createInMemoryWebView,
} from '@objectifthunes/limestone-sdk/testing';
State manipulation example
import { createInMemoryBiometrics } from '@objectifthunes/limestone-sdk/testing';
const biometrics = createInMemoryBiometrics();
// Simulate hardware absent
biometrics.state.available = false;
// Simulate a failed attempt
biometrics.state.nextResult = { success: false, error: 'locked' };
const result = await biometrics.authenticate({ promptMessage: 'Confirm identity' });
expect(result.success).toBe(false);
expect(result.error).toBe('locked');
// Simulate success
biometrics.state.available = true;
biometrics.state.nextResult = { success: true };
const ok = await biometrics.authenticate();
expect(ok.success).toBe(true);
Every double follows the same pattern: create, mutate state, call port methods, assert. See the Testing guide for the full createTestApp API that wires all 16 doubles in a single call.
R3F adapters
The ./r3f subpath exports render-prop factories backed by React Three Fiber for the six 3D components. Install the peer dependencies before using:
npx expo install three @react-three/fiber @react-three/drei
| Factory | Component |
|---|---|
createR3FScene3D | Scene3D |
createR3FModelViewer | ModelViewer |
createR3FObjectScanner | ObjectScanner |
createR3FBeforeAfter3D | BeforeAfter3D |
createR3FPanorama360 | Panorama360 |
createR3FMapScene3D | MapScene3D |
import {
createR3FScene3D,
createR3FModelViewer,
createR3FPanorama360,
} from '@objectifthunes/limestone-sdk/r3f';
import { Scene3D, ModelViewer, Panorama360 } from '@objectifthunes/limestone-sdk';
<Scene3D renderScene={createR3FScene3D()} cameraPosition={[0, 0, 5]} fov={75} />
<ModelViewer renderModel={createR3FModelViewer()} autoRotate autoRotateSpeed={1.5} />
<Panorama360 renderPanorama={createR3FPanorama360()} enableGyroscope />
ViroReact adapters
The ./viro subpath exports render-prop factories backed by @reactvision/react-viro for the eight AR components. Install the peer dependency before using:
npx expo install @reactvision/react-viro
| Factory | Component |
|---|---|
createViroARView | ARView |
createViroARTryOn | ARTryOn |
createViroARPlacement | ARPlacement |
createViroARMeasure | ARMeasure |
createViroARAnnotation | ARAnnotation |
createViroARWorldObject | ARWorldObject |
createViroARNavigation | ARNavigation |
createViroARBodyMeasure | ARBodyMeasure |
import {
createViroARView,
createViroARPlacement,
createViroARMeasure,
} from '@objectifthunes/limestone-sdk/viro';
import { ARView, ARPlacement, ARMeasure } from '@objectifthunes/limestone-sdk';
<ARView renderAR={createViroARView()} showInstructions showReticle />
<ARPlacement renderPlacement={createViroARPlacement()} showSurfaceIndicator confirmPlacement />
<ARMeasure renderMeasure={createViroARMeasure()} unit="cm" onMeasurement={(m) => console.log(m)} />