Expo Adapters

Adapter table

SubpathPortExpo ModuleFactory
./expo-biometricsBiometricProviderexpo-local-authenticationcreateExpoBiometrics
./expo-cameraCameraProviderexpo-cameracreateExpoCamera
./expo-secure-storeSecureStorageProviderexpo-secure-storecreateExpoSecureStore
./expo-hapticsHapticsProviderexpo-hapticscreateExpoHaptics
./expo-notificationsNotificationProviderexpo-notificationscreateExpoNotifications
./expo-purchasesPurchaseProviderreact-native-purchasescreateExpoPurchases
./expo-permissionsPermissionsProviderexpo-modules-corecreateExpoPermissions
./expo-sharingShareProviderexpo-sharingcreateExpoSharing
./expo-clipboardClipboardProviderexpo-clipboardcreateExpoClipboard
./expo-locationLocationProviderexpo-locationcreateExpoLocation
./expo-media-libraryMediaLibraryProviderexpo-media-librarycreateExpoMediaLibrary
./expo-mapMapProviderreact-native-mapscreateExpoMap
./expo-web-browserWebBrowserProviderexpo-web-browsercreateExpoWebBrowser
./expo-contactsContactsProviderexpo-contactscreateExpoContacts
./expo-reviewReviewProviderexpo-store-reviewcreateExpoReview
./expo-keychainKeychainProviderexpo-secure-storecreateExpoKeychain
./expo-videoVideoPlayer (render-prop)expo-avcreateExpoVideoPlayer
./expo-qr-scannerQRScanner (render-prop)expo-cameracreateExpoQRScanner
./expo-web-viewWebView (render-prop)react-native-webviewcreateExpoWebView

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
FactoryComponent
createR3FScene3DScene3D
createR3FModelViewerModelViewer
createR3FObjectScannerObjectScanner
createR3FBeforeAfter3DBeforeAfter3D
createR3FPanorama360Panorama360
createR3FMapScene3DMapScene3D
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
FactoryComponent
createViroARViewARView
createViroARTryOnARTryOn
createViroARPlacementARPlacement
createViroARMeasureARMeasure
createViroARAnnotationARAnnotation
createViroARWorldObjectARWorldObject
createViroARNavigationARNavigation
createViroARBodyMeasureARBodyMeasure
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)} />