# Mobile App Best Practices for Future Implementation ## React Native Implementation Readiness Guide --- ## Overview This document outlines best practices, architectural patterns, and implementation guidelines for building the native mobile apps (iOS & Android) using React Native. The current web implementation provides a solid foundation that can be leveraged for the mobile apps. ### Current Implementation Status - ✅ **Web App (maternal-web)**: Fully implemented with Next.js 14 - ✅ **Backend API (maternal-app-backend)**: Complete with REST + WebSocket - ⏳ **Mobile Apps**: Not yet implemented (planned) ### Technology Stack for Mobile ```javascript { "react-native": "^0.73.0", "expo": "~50.0.0", "@react-navigation/native": "^6.1.0", "@react-navigation/stack": "^6.3.0", "react-native-paper": "^5.12.0", "redux-toolkit": "^2.0.0", "react-native-reanimated": "^3.6.0", "expo-secure-store": "~12.8.0", "expo-notifications": "~0.27.0" } ``` --- ## Architecture Principles ### 1. Code Reusability Between Web and Mobile **Shared Business Logic** ```typescript // ✅ GOOD: Platform-agnostic business logic // libs/shared/src/services/activityService.ts export class ActivityService { async logActivity(data: ActivityData): Promise { // Platform-independent logic return this.apiClient.post('/activities', data); } } // Can be used in both web and mobile ``` **Platform-Specific UI** ```typescript // ❌ BAD: Mixing UI and logic function TrackingButton() { const [activity, setActivity] = useState(); // Business logic mixed with UI } // ✅ GOOD: Separate concerns // hooks/useActivityTracking.ts export function useActivityTracking() { // Reusable logic } // web/components/TrackingButton.tsx // mobile/components/TrackingButton.tsx // Different UI, same logic via hook ``` **Recommended Project Structure** ``` maternal-app-monorepo/ ├── apps/ │ ├── web/ # Next.js web app (existing) │ ├── mobile/ # React Native mobile app (future) │ └── backend/ # NestJS API (existing) ├── packages/ │ ├── shared/ # Shared between web & mobile │ │ ├── api-client/ # API communication │ │ ├── state/ # Redux store & slices │ │ ├── hooks/ # Custom React hooks │ │ ├── utils/ # Utilities │ │ └── types/ # TypeScript definitions │ ├── ui-components/ # Platform-specific UI │ │ ├── web/ │ │ └── mobile/ │ └── constants/ # Shared constants └── tools/ # Build tools & scripts ``` --- ## Mobile-Specific Features ### 1. Offline-First Architecture **Local Database: SQLite** ```typescript // Mobile: Use SQLite for offline storage import * as SQLite from 'expo-sqlite'; const db = SQLite.openDatabase('maternal.db'); // Sync queue for offline operations interface SyncQueueItem { id: string; operation: 'CREATE' | 'UPDATE' | 'DELETE'; entity: 'activity' | 'child' | 'family'; data: any; timestamp: Date; retryCount: number; } // Auto-sync when connection restored export class OfflineSyncService { async syncPendingChanges() { const pendingItems = await this.getSyncQueue(); for (const item of pendingItems) { try { await this.syncItem(item); await this.removefromQueue(item.id); } catch (error) { await this.incrementRetryCount(item.id); } } } } ``` **Conflict Resolution** ```typescript // Last-write-wins with timestamp comparison export class ConflictResolver { resolve(local: Activity, remote: Activity): Activity { const localTime = new Date(local.updatedAt); const remoteTime = new Date(remote.updatedAt); // Use latest version return localTime > remoteTime ? local : remote; } } ``` ### 2. Push Notifications **Expo Notifications Setup** ```typescript import * as Notifications from 'expo-notifications'; import * as Device from 'expo-device'; export class NotificationService { async registerForPushNotifications() { if (!Device.isDevice) { return null; } const { status: existingStatus } = await Notifications.getPermissionsAsync(); let finalStatus = existingStatus; if (existingStatus !== 'granted') { const { status } = await Notifications.requestPermissionsAsync(); finalStatus = status; } if (finalStatus !== 'granted') { return null; } const token = ( await Notifications.getExpoPushTokenAsync({ projectId: 'your-expo-project-id' }) ).data; // Send token to backend await this.apiClient.post('/users/push-token', { token }); return token; } // Configure notification behavior configureNotifications() { Notifications.setNotificationHandler({ handleNotification: async () => ({ shouldShowAlert: true, shouldPlaySound: true, shouldSetBadge: true, }), }); } } ``` **Notification Categories** ```typescript // Backend: Define notification types export enum NotificationType { FAMILY_UPDATE = 'family_update', ACTIVITY_REMINDER = 'activity_reminder', MILESTONE_REACHED = 'milestone_reached', AI_INSIGHT = 'ai_insight', SYNC_COMPLETE = 'sync_complete', } // Mobile: Handle notification tap Notifications.addNotificationResponseReceivedListener(response => { const { type, data } = response.notification.request.content; switch (type) { case NotificationType.FAMILY_UPDATE: navigation.navigate('Family', { familyId: data.familyId }); break; case NotificationType.ACTIVITY_REMINDER: navigation.navigate('Track', { type: data.activityType }); break; // ... handle other types } }); ``` ### 3. Biometric Authentication **Face ID / Touch ID / Fingerprint** ```typescript import * as LocalAuthentication from 'expo-local-authentication'; import * as SecureStore from 'expo-secure-store'; export class BiometricAuthService { async isBiometricAvailable(): Promise { const compatible = await LocalAuthentication.hasHardwareAsync(); const enrolled = await LocalAuthentication.isEnrolledAsync(); return compatible && enrolled; } async authenticateWithBiometrics(): Promise { const result = await LocalAuthentication.authenticateAsync({ promptMessage: 'Authenticate to access Maternal App', fallbackLabel: 'Use passcode', }); return result.success; } async enableBiometricLogin(userId: string, token: string) { // Store refresh token securely await SecureStore.setItemAsync( `auth_token_${userId}`, token, { keychainAccessible: SecureStore.WHEN_UNLOCKED_THIS_DEVICE_ONLY, } ); // Enable biometric flag await SecureStore.setItemAsync( 'biometric_enabled', 'true' ); } async loginWithBiometrics(): Promise { const authenticated = await this.authenticateWithBiometrics(); if (!authenticated) { return null; } // Retrieve stored token const userId = await SecureStore.getItemAsync('current_user_id'); const token = await SecureStore.getItemAsync(`auth_token_${userId}`); return token; } } ``` ### 4. Voice Input (Whisper) **React Native Voice** ```typescript import Voice from '@react-native-voice/voice'; export class VoiceInputService { constructor() { Voice.onSpeechResults = this.onSpeechResults; Voice.onSpeechError = this.onSpeechError; } async startListening() { try { await Voice.start('en-US'); } catch (error) { console.error('Voice start error:', error); } } async stopListening() { try { await Voice.stop(); } catch (error) { console.error('Voice stop error:', error); } } onSpeechResults = (event: any) => { const transcript = event.value[0]; // Send to backend for processing with Whisper this.processTranscript(transcript); }; onSpeechError = (event: any) => { console.error('Speech error:', event.error); }; async processTranscript(transcript: string) { // Send to backend Whisper API const response = await fetch('/api/v1/voice/transcribe', { method: 'POST', body: JSON.stringify({ transcript }), }); const { activityData } = await response.json(); return activityData; } } ``` ### 5. Camera & Photo Upload **Expo Image Picker** ```typescript import * as ImagePicker from 'expo-image-picker'; export class PhotoService { async requestPermissions() { const { status } = await ImagePicker.requestMediaLibraryPermissionsAsync(); if (status !== 'granted') { Alert.alert( 'Permission needed', 'Please allow access to photos' ); return false; } return true; } async pickImage() { const hasPermission = await this.requestPermissions(); if (!hasPermission) return null; const result = await ImagePicker.launchImageLibraryAsync({ mediaTypes: ImagePicker.MediaTypeOptions.Images, allowsEditing: true, aspect: [4, 3], quality: 0.8, }); if (!result.canceled) { return result.assets[0].uri; } return null; } async takePhoto() { const { status } = await ImagePicker.requestCameraPermissionsAsync(); if (status !== 'granted') { return null; } const result = await ImagePicker.launchCameraAsync({ allowsEditing: true, aspect: [4, 3], quality: 0.8, }); if (!result.canceled) { return result.assets[0].uri; } return null; } async uploadPhoto(uri: string, childId: string) { const formData = new FormData(); formData.append('file', { uri, type: 'image/jpeg', name: 'photo.jpg', } as any); formData.append('childId', childId); const response = await fetch('/api/v1/children/photo', { method: 'POST', body: formData, headers: { 'Content-Type': 'multipart/form-data', }, }); return response.json(); } } ``` --- ## Performance Optimization ### 1. List Virtualization **FlatList for Large Datasets** ```typescript import { FlatList } from 'react-native'; // ✅ GOOD: Virtualized list for activities } keyExtractor={(item) => item.id} // Performance optimizations removeClippedSubviews={true} maxToRenderPerBatch={10} updateCellsBatchingPeriod={50} initialNumToRender={10} windowSize={5} // Pull to refresh onRefresh={handleRefresh} refreshing={isRefreshing} // Infinite scroll onEndReached={loadMore} onEndReachedThreshold={0.5} /> // ❌ BAD: Rendering all items at once {activities.map(activity => )} ``` ### 2. Image Optimization **React Native Fast Image** ```typescript import FastImage from 'react-native-fast-image'; // ✅ GOOD: Optimized image loading // Preload images for better UX FastImage.preload([ { uri: photo1 }, { uri: photo2 }, ]); ``` ### 3. Animation Performance **React Native Reanimated 3** ```typescript import Animated, { useSharedValue, useAnimatedStyle, withSpring, } from 'react-native-reanimated'; // ✅ GOOD: Run on UI thread function AnimatedButton() { const scale = useSharedValue(1); const animatedStyle = useAnimatedStyle(() => ({ transform: [{ scale: scale.value }], })); const handlePress = () => { scale.value = withSpring(0.95, {}, () => { scale.value = withSpring(1); }); }; return ( Track Activity ); } ``` ### 4. Bundle Size Optimization **Hermes Engine (for Android)** ```javascript // android/app/build.gradle project.ext.react = [ enableHermes: true, // Enable Hermes engine ] // Results in: // - Faster startup time // - Lower memory usage // - Smaller APK size ``` **Code Splitting** ```typescript // Lazy load heavy screens const AIAssistant = lazy(() => import('./screens/AIAssistant')); const Analytics = lazy(() => import('./screens/Analytics')); // Use with Suspense }> ``` --- ## Testing Strategy for Mobile ### Unit Tests (Jest) ```typescript import { renderHook, act } from '@testing-library/react-hooks'; import { useActivityTracking } from './useActivityTracking'; describe('useActivityTracking', () => { it('should track activity successfully', async () => { const { result } = renderHook(() => useActivityTracking()); await act(async () => { await result.current.logActivity({ type: 'feeding', childId: 'child_123', }); }); expect(result.current.activities).toHaveLength(1); }); }); ``` ### Component Tests (React Native Testing Library) ```typescript import { render, fireEvent } from '@testing-library/react-native'; import { TrackingButton } from './TrackingButton'; describe('TrackingButton', () => { it('should handle press event', () => { const onPress = jest.fn(); const { getByText } = render( ); fireEvent.press(getByText('Track Feeding')); expect(onPress).toHaveBeenCalled(); }); }); ``` ### E2E Tests (Detox) ```typescript describe('Activity Tracking Flow', () => { beforeAll(async () => { await device.launchApp(); }); it('should log a feeding activity', async () => { await element(by.id('track-feeding-btn')).tap(); await element(by.id('amount-input')).typeText('120'); await element(by.id('save-btn')).tap(); await expect(element(by.text('Activity saved'))).toBeVisible(); }); }); ``` --- ## Platform-Specific Considerations ### iOS Specific **1. App Store Guidelines** ```markdown - ✅ Submit privacy manifest (PrivacyInfo.xcprivacy) - ✅ Declare data collection practices - ✅ Request permissions with clear explanations - ✅ Support all device sizes (iPhone, iPad) - ✅ Dark mode support required ``` **2. iOS Permissions** ```xml NSCameraUsageDescription Take photos of your child's milestones NSPhotoLibraryUsageDescription Save and view photos of your child NSMicrophoneUsageDescription Use voice to log activities hands-free NSFaceIDUsageDescription Use Face ID for quick and secure login ``` **3. iOS Background Modes** ```xml UIBackgroundModes remote-notification fetch ``` ### Android Specific **1. Permissions** ```xml ``` **2. ProGuard (Code Obfuscation)** ``` # android/app/proguard-rules.pro -keep class com.maternalapp.** { *; } -keepclassmembers class * { @com.facebook.react.uimanager.annotations.ReactProp ; } ``` **3. App Signing** ```bash # Generate release keystore keytool -genkeypair -v -storetype PKCS12 \ -keystore maternal-app-release.keystore \ -alias maternal-app \ -keyalg RSA -keysize 2048 \ -validity 10000 ``` --- ## Deployment & Distribution ### App Store (iOS) **1. Build Configuration** ```bash # Install dependencies cd ios && pod install # Build for production xcodebuild -workspace MaternalApp.xcworkspace \ -scheme MaternalApp \ -configuration Release \ -archivePath MaternalApp.xcarchive \ archive # Export IPA xcodebuild -exportArchive \ -archivePath MaternalApp.xcarchive \ -exportPath ./build \ -exportOptionsPlist ExportOptions.plist ``` **2. TestFlight (Beta Testing)** ```bash # Upload to TestFlight xcrun altool --upload-app \ --type ios \ --file MaternalApp.ipa \ --username "developer@example.com" \ --password "@keychain:AC_PASSWORD" ``` ### Google Play (Android) **1. Build AAB (Android App Bundle)** ```bash cd android ./gradlew bundleRelease # Output: android/app/build/outputs/bundle/release/app-release.aab ``` **2. Internal Testing Track** ```bash # Upload to Google Play Console # Use Fastlane or manual upload ``` ### Over-the-Air Updates (CodePush) **Setup for rapid iteration** ```bash # Install CodePush CLI npm install -g code-push-cli # Register app code-push app add maternal-app-ios ios react-native code-push app add maternal-app-android android react-native # Release update code-push release-react maternal-app-ios ios \ -d Production \ --description "Bug fixes and performance improvements" ``` **Rollback Strategy** ```bash # Rollback to previous version if issues detected code-push rollback maternal-app-ios Production # Monitor adoption rate code-push deployment ls maternal-app-ios ``` --- ## Monitoring & Analytics ### Crash Reporting (Sentry) ```typescript import * as Sentry from '@sentry/react-native'; Sentry.init({ dsn: 'YOUR_SENTRY_DSN', environment: __DEV__ ? 'development' : 'production', tracesSampleRate: 1.0, }); // Automatic breadcrumbs Sentry.addBreadcrumb({ category: 'activity', message: 'User logged feeding activity', level: 'info', }); // Custom error context Sentry.setContext('user', { id: user.id, familyId: family.id, }); ``` ### Performance Monitoring ```typescript import * as Sentry from '@sentry/react-native'; // Monitor screen load time const transaction = Sentry.startTransaction({ name: 'ActivityTrackingScreen', op: 'navigation', }); // ... screen loads ... transaction.finish(); // Monitor specific operations const span = transaction.startChild({ op: 'api.call', description: 'Log activity', }); await logActivity(data); span.finish(); ``` ### Usage Analytics ```typescript // Integrate with backend analytics service import { Analytics } from '@maternal/shared/analytics'; Analytics.track('Activity Logged', { type: 'feeding', method: 'voice', duration: 15000, }); Analytics.screen('Activity Tracking'); Analytics.identify(user.id, { familySize: family.members.length, childrenCount: children.length, isPremium: subscription.isPremium, }); ``` --- ## Accessibility (WCAG AA Compliance) ### Screen Reader Support ```typescript import { View, Text, TouchableOpacity } from 'react-native'; Track Feeding ``` ### Dynamic Font Sizes ```typescript import { Text, useWindowDimensions } from 'react-native'; // Respect user's font size preferences Activity logged successfully ``` ### Color Contrast ```typescript // Ensure WCAG AA compliance (4.5:1 ratio for normal text) const colors = { primary: '#FF8B7D', // Coral primaryText: '#1A1A1A', // Dark text on light background background: '#FFFFFF', textOnPrimary: '#FFFFFF', // White text on coral }; // Validate contrast ratios in design system ``` --- ## Security Best Practices ### Secure Storage ```typescript import * as SecureStore from 'expo-secure-store'; // ✅ GOOD: Encrypted storage for sensitive data await SecureStore.setItemAsync('auth_token', token); // ❌ BAD: AsyncStorage for sensitive data (unencrypted) await AsyncStorage.setItem('auth_token', token); ``` ### Certificate Pinning ```typescript // Prevent man-in-the-middle attacks import { configureCertificatePinning } from 'react-native-cert-pinner'; await configureCertificatePinning([ { hostname: 'api.maternalapp.com', certificates: [ 'sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=', ], }, ]); ``` ### Jailbreak/Root Detection ```typescript import JailMonkey from 'jail-monkey'; if (JailMonkey.isJailBroken()) { Alert.alert( 'Security Warning', 'This app may not function properly on jailbroken devices' ); } ``` --- ## Migration Path from Web to Mobile ### Phase 1: Extract Shared Logic ```typescript // 1. Move business logic to shared package // packages/shared/src/services/ export class ActivityService { ... } export class AIService { ... } // 2. Update web app to use shared package import { ActivityService } from '@maternal/shared'; ``` ### Phase 2: Build Mobile Shell ```typescript // 1. Create React Native app with Expo npx create-expo-app maternal-mobile // 2. Set up navigation structure // 3. Integrate shared services // 4. Build basic UI with React Native Paper ``` ### Phase 3: Implement Mobile-Specific Features ```typescript // 1. Offline mode with SQLite // 2. Push notifications // 3. Biometric auth // 4. Voice input // 5. Camera integration ``` ### Phase 4: Testing & Optimization ```typescript // 1. Unit tests // 2. Component tests // 3. E2E tests with Detox // 4. Performance profiling // 5. Accessibility audit ``` ### Phase 5: Beta Testing & Launch ```typescript // 1. TestFlight (iOS) // 2. Google Play Internal Testing // 3. Gather feedback // 4. Iterate based on metrics // 5. Production launch ``` --- ## Conclusion This guide provides a comprehensive roadmap for implementing native mobile apps. Key takeaways: 1. **Code Reusability**: Share business logic between web and mobile 2. **Offline-First**: Essential for mobile UX 3. **Native Features**: Leverage platform-specific capabilities 4. **Performance**: Optimize for mobile constraints 5. **Testing**: Comprehensive strategy for quality 6. **Security**: Protect user data on mobile devices 7. **Analytics**: Track usage and iterate The current web implementation already follows many mobile-friendly patterns, making the transition to React Native straightforward when the time comes.