Files
self/docs/development-patterns.md
Justin Hernandez bd8d425cc7 SELF-763: remove aesop features for now (#1132)
* clean onboarding camera screens

* remove remaining aesop artifacts. fix tests
2025-09-28 07:55:11 -07:00

5.6 KiB

Self App Development Patterns

React Native Architecture

Navigation System

The app uses @react-navigation/native with createStaticNavigation for type-safe navigation. Screens are organized by feature modules and use platform-specific initial routes.

// Navigation setup pattern
export const navigationScreens = {
  ...systemScreens,
  ...passportScreens,
  ...homeScreens,
  ...proveScreens,
  ...settingsScreens,
  ...recoveryScreens,
  ...devScreens,
};

// Platform-specific initial routes
initialRouteName: Platform.OS === 'web' ? 'Home' : 'Splash'

Modal System

Custom modal system using useModal hook with callback registry for handling button presses and dismissals.

const { showModal, dismissModal, visible } = useModal({
  titleText: 'Modal Title',
  bodyText: 'Modal content',
  buttonText: 'Action',
  onButtonPress: async () => {
    // Handle button press
  },
  onModalDismiss: () => {
    // Handle modal dismiss
  },
});

Platform-Specific Handling

Always check platform before implementing platform-specific code:

if (Platform.OS === 'ios') {
  // iOS-specific implementation
} else {
  // Android-specific implementation
}

Native Module Initialization

Critical for NFC and other native functionality:

// Initialize native modules before any native operations
const modulesReady = await initializeNativeModules();
if (!modulesReady) {
  console.warn('Native modules not ready, proceeding with limited functionality');
}

State Management

Hook-Based State

  • Custom hooks for complex state management (useModal, useHapticNavigation)
  • Zustand for global state management
  • React Navigation state for screen-specific data

Data Persistence

  • AsyncStorage: Simple key-value storage
  • SQLite: Complex data (proof history)
  • Keychain: Sensitive data (biometrics, encryption keys)

Testing Conventions

Jest Configuration

  • Comprehensive mocks in jest.setup.js for all native modules
  • Module mapping for clean imports
  • Test-specific TypeScript configuration

Mock Patterns

All React Native native modules are mocked with realistic return values:

jest.mock('react-native-keychain', () => ({
  SECURITY_LEVEL_ANY: 'MOCK_SECURITY_LEVEL_ANY',
  setGenericPassword: jest.fn(),
  getGenericPassword: jest.fn(),
  resetGenericPassword: jest.fn(),
  ACCESSIBLE: {
    WHEN_UNLOCKED: 'AccessibleWhenUnlocked',
    // ... other constants
  },
}));

Testing Patterns

  • Use renderHook for custom hook testing
  • Mock console.error to avoid test output clutter
  • Test error boundaries and recovery mechanisms
  • E2E testing with Maestro for platform-specific flows

NFC Implementation

Cross-Platform Architecture

  • iOS: Custom PassportReader Swift module
  • Android: Custom RNPassportReaderModule Kotlin implementation
  • Unified JavaScript interface with platform detection

Authentication Methods

  • MRZ Key: Derived from passport number, DOB, and expiry date
  • CAN (Card Access Number): 6-digit number for PACE authentication
  • PACE: Password Authenticated Connection Establishment
  • BAC Fallback: Basic Access Control when PACE fails

Error Handling

  • Multiple BAC attempts with delays
  • Graceful degradation from PACE to BAC
  • Real-time status updates and haptic feedback
  • Comprehensive error boundaries

JavaScript Interface

export const scan = async (inputs: Inputs) => {
  return Platform.OS === 'android'
    ? await scanAndroid(inputs)
    : await scanIOS(inputs);
};

Code Organization

File Structure

src/
├── components/          # Reusable UI components
├── screens/            # Screen components by feature
├── navigation/         # Navigation configuration
├── hooks/              # Custom React hooks
├── utils/              # Shared utilities
├── types/              # TypeScript type definitions
├── stores/             # State management
└── providers/          # Context providers

Import Patterns

  • Use @/ alias for src imports: import { Component } from '@/components'
  • Use @tests/ alias for test imports: import { mockData } from '@tests/utils'
  • Platform-specific imports with conditional rendering

Build & Deployment

Scripts

  • yarn ios / yarn android for platform-specific builds
  • yarn test for Jest testing
  • yarn test:e2e:ios / yarn test:e2e:android for E2E
  • Fastlane for deployment automation

Dependencies

  • Yarn workspaces for monorepo management
  • Platform-specific native modules
  • Tamagui for UI components
  • React Navigation for routing

Security & Privacy

Data Protection

  • Sensitive data not logged in production
  • Secure storage with Keychain
  • Proper cleanup of sensitive data
  • Certificate validation for passport data

Privacy Features

  • Zero-knowledge proof generation
  • Selective attribute revelation
  • Privacy-preserving age verification
  • Identity commitment privacy

Common Patterns

Error Handling

try {
  const result = await riskyOperation();
  return result;
} catch (error) {
  console.error('Operation failed:', error);
  // Graceful degradation
  return fallbackValue;
}

Performance Optimization

  • Lazy load screens and components
  • Bundle size optimization with tree shaking
  • Memory leak prevention in native modules
  • Proper cleanup in useEffect and component unmount

Platform Differences

  • Always check Platform.OS before platform-specific code
  • Different implementations for iOS/Android when needed
  • Platform-specific testing strategies
  • Conditional rendering for platform differences