Skip to content

Start typing to search the documentation.

Complete Example (Legacy)

On this page

A single runnable integration you can copy into a fresh React Native app: dev-stub adapters, event listeners wired to a navigator, and the screens assembled into one App.

This page is only the assembly. The reference material it builds on lives elsewhere and is not repeated here:

1. Adapters

All five adapters are required (auth, scanner, network, crypto, documents). The stubs below are the smallest set that runs — see Required Adapters for what each one is actually for and what a production implementation needs.

import {
  createListenersMap,
  SdkEvents,
  SelfClientProvider,
  webNFCScannerShim,
  type Adapters,
  type Config,
} from '@selfxyz/mobile-sdk-alpha';

const createAdapters = (): Adapters => ({
  auth: {
    // you MUST provide a private key to the sdk that will be used when generating the zk circuits
    async getPrivateKey(): Promise<string | null> {
      // In production, get from secure storage
      return '0x' + 'a'.repeat(64); // Dummy key for demo
    },
  },

  scanner: webNFCScannerShim, // Use web shim for development

  network: {
    http: {
      fetch: (input: RequestInfo, init?: RequestInit) => fetch(input, init),
    },
    ws: {
      connect: (url: string) => {
        const socket = new WebSocket(url);
        return {
          send: (data) => socket.send(data),
          close: () => socket.close(),
          onMessage: (cb) => socket.addEventListener('message', (ev) => cb(ev.data)),
          onError: (cb) => socket.addEventListener('error', cb),
          onClose: (cb) => socket.addEventListener('close', cb),
        };
      },
    },
  },

  crypto: {
    async hash(data: Uint8Array): Promise<Uint8Array> {
      // Use Web Crypto API
      const buffer = await crypto.subtle.digest('SHA-256', data);
      return new Uint8Array(buffer);
    },
    async sign(_data: Uint8Array, _keyRef: string): Promise<Uint8Array> {
      throw new Error('Signing not implemented in minimal example');
    },
  },

  documents: {
    async loadDocumentCatalog() {
      return { documents: [] };
    },
    async saveDocumentCatalog(catalog) {
      console.log('Save catalog:', catalog);
    },
    async loadDocumentById(id: string) {
      return null;
    },
    async saveDocument(id: string, document) {
      console.log('Save document:', id, document);
    },
    async deleteDocument(id: string) {
      console.log('Delete document:', id);
    },
  },

  // Optional: minimal analytics
  analytics: {
    trackEvent: (event: string, payload?: any) => {
      console.log('Analytics:', event, payload);
    },
  },
});

2. Navigation listeners

The SDK is event-driven: screens emit events, and your listeners decide where to go next. Building them in a factory that takes the navigator keeps the routing in one place.

function createNavigationListeners(navigation) {
  const { map, addListener } = createListenersMap();

  // Country selection -> ID picker
  addListener(SdkEvents.DOCUMENT_COUNTRY_SELECTED, ({ countryCode, documentTypes }) => {
    navigation.navigate('IDPicker', { countryCode, documentTypes });
  });

  // Document type selection -> appropriate flow
  addListener(SdkEvents.DOCUMENT_TYPE_SELECTED, ({ documentType, countryCode }) => {
    switch (documentType) {
      case 'p': // Passport
      case 'i': // ID Card
        navigation.navigate('DocumentCamera');
        break;
      case 'a': // Aadhaar
        navigation.navigate('AadhaarUpload', { countryCode });
        break;
      case 'kyc': // KYC
        navigation.navigate('KycIntro', { countryCode });
        break;
      default:
        navigation.navigate('ComingSoon', { documentType, countryCode });
    }
  });

  // MRZ scan success -> NFC scanning
  addListener(SdkEvents.DOCUMENT_MRZ_READ_SUCCESS, () => {
    navigation.navigate('NFCScan');
  });

  // MRZ scan failure -> troubleshooting
  addListener(SdkEvents.DOCUMENT_MRZ_READ_FAILURE, () => {
    navigation.navigate('DocumentTrouble');
  });

  return map;
}

Event flow

CountryPickerScreen
    ↓ (DOCUMENT_COUNTRY_SELECTED)
IDSelectionScreen
    ↓ (DOCUMENT_TYPE_SELECTED)
DocumentCameraScreen
    ↓ (DOCUMENT_MRZ_READ_SUCCESS)
[Your NFC Screen]

3. Screen wrappers

Each SDK screen is imported from its own path and wrapped so the navigator can pass it route params and back handlers.

import { SafeAreaView } from 'react-native';
import { DocumentCameraScreen } from '@selfxyz/mobile-sdk-alpha/onboarding/document-camera-screen';
import IDSelectionScreen from '@selfxyz/mobile-sdk-alpha/onboarding/id-selection-screen';
import SDKCountryPickerScreen from '@selfxyz/mobile-sdk-alpha/onboarding/country-picker-screen';

function CountryPickerScreen() {
  return (
    <SafeAreaView style={{ flex: 1 }}>
      <SDKCountryPickerScreen />
    </SafeAreaView>
  );
}

function IDPickerScreen({ route }) {
  const { countryCode, documentTypes } = route.params;

  return (
    <SafeAreaView style={{ flex: 1 }}>
      <IDSelectionScreen countryCode={countryCode} documentTypes={documentTypes} />
    </SafeAreaView>
  );
}

function DocumentCameraStackScreen({ navigation }) {
  return (
    <SafeAreaView style={{ flex: 1 }}>
      <DocumentCameraScreen
        onBack={() => navigation.goBack()}
        onSuccess={() => {
          console.log('MRZ scan successful!');
        }}
      />
    </SafeAreaView>
  );
}

4. Assemble the app

SelfClientProvider wraps everything, so every SDK screen must render inside it.

import React, { useMemo } from 'react';
import { NavigationContainer, createNavigationContainerRef } from '@react-navigation/native';
import { createStackNavigator } from '@react-navigation/stack';

const Stack = createStackNavigator();
const navigationRef = createNavigationContainerRef();

export default function App() {
  const config: Config = useMemo(() => ({}), []);
  const adapters: Adapters = useMemo(() => createAdapters(), []);
  const listeners = useMemo(() => createNavigationListeners(navigationRef), []);

  return (
    <SelfClientProvider config={config} adapters={adapters} listeners={listeners}>
      <NavigationContainer ref={navigationRef}>
        <Stack.Navigator initialRouteName="CountryPicker">
          <Stack.Screen
            name="CountryPicker"
            component={CountryPickerScreen}
            options={{ title: 'Select Country' }}
          />
          <Stack.Screen
            name="IDPicker"
            component={IDPickerScreen}
            options={{ title: 'Select Document Type' }}
          />
          <Stack.Screen
            name="DocumentCamera"
            component={DocumentCameraStackScreen}
            options={{ title: 'Scan Document' }}
          />
        </Stack.Navigator>
      </NavigationContainer>
    </SelfClientProvider>
  );
}

The AadhaarUpload, KycIntro, NFCScan, DocumentTrouble, and ComingSoon routes referenced by the listeners are yours to build — register them alongside the three above as you add each flow.

Was this page helpful?