| # Phase 2.4: Mobile App (React Native) | |
| ## Overview | |
| **Phase 2.4** extends the platform to mobile devices with a **native React Native application** for iOS and Android, featuring: | |
| - π± Native mobile UI (iOS/Android) | |
| - π¬ Real-time chat with push notifications | |
| - π Care plan management | |
| - π Health indicator tracking | |
| - π Biometric authentication | |
| - π΄ Offline-first architecture | |
| - π Automatic sync when online | |
| **Prerequisites:** | |
| - Phase 2.1 (PostgreSQL Database) | |
| - Phase 2.2 (Analytics) | |
| - Phase 2.3 (FHIR Integration) - optional | |
| --- | |
| ## Architecture | |
| ``` | |
| βββββββββββββββββββββββββββββββββββββββββββ | |
| β iOS App (Swift/Objective-C) β | |
| βββββββββββββββββββββββββββββββββββββββββββ€ | |
| β React Native Bridge β | |
| βββββββββββββββββββββββββββββββββββββββββββ€ | |
| β JavaScript/TypeScript Layer β | |
| β (App.tsx, screens, components) β | |
| βββββββββββββββββββββββββββββββββββββββββββ€ | |
| β Redux State Management β | |
| β (Store, reducers, actions) β | |
| βββββββββββββββββββββββββββββββββββββββββββ€ | |
| β API Client & Sync Engine β | |
| βββββββββββββββββββββββββββββββββββββββββββ€ | |
| β Local Storage (SQLite/AsyncStorage) β | |
| βββββββββββββββββββββββββββββββββββββββββββ€ | |
| β Backend APIs β | |
| β (app.py, app_phase2.py) β | |
| βββββββββββββββββββββββββββββββββββββββββββ€ | |
| β PostgreSQL Database β | |
| βββββββββββββββββββββββββββββββββββββββββββ | |
| ``` | |
| --- | |
| ## Setup Instructions | |
| ### 1. Environment Setup | |
| ```bash | |
| # Install Node.js and npm (if not already installed) | |
| node --version # Should be 18+ | |
| npm --version # Should be 8+ | |
| # Install React Native CLI | |
| npm install -g react-native-cli | |
| # Install iOS/Android requirements | |
| # macOS: Install Xcode and CocoaPods | |
| xcode-select --install | |
| sudo gem install cocoapods | |
| # Android: Install Android Studio | |
| # https://developer.android.com/studio | |
| ``` | |
| ### 2. Create Mobile Project | |
| ```bash | |
| # Navigate to mobile directory | |
| cd mobile | |
| # Install dependencies | |
| npm install | |
| # Install Pods (iOS) | |
| cd ios && pod install && cd .. | |
| ``` | |
| ### 3. Configure Backend Connection | |
| ```bash | |
| # Create .env file | |
| cat > .env << 'EOF' | |
| REACT_APP_API_URL=https://your-backend.com | |
| REACT_APP_API_TIMEOUT=10000 | |
| REACT_APP_FIREBASE_API_KEY=your_firebase_key | |
| REACT_APP_FIREBASE_PROJECT_ID=your_project_id | |
| REACT_APP_ENABLE_OFFLINE=true | |
| REACT_APP_ENABLE_PUSH_NOTIFICATIONS=true | |
| EOF | |
| ``` | |
| ### 4. Run on Simulator/Device | |
| ```bash | |
| # iOS Simulator | |
| npm run ios | |
| # or | |
| react-native run-ios | |
| # Android Emulator | |
| npm run android | |
| # or | |
| react-native run-android | |
| # Device (iOS) | |
| react-native run-ios --device "Device Name" | |
| # Device (Android) | |
| react-native run-android | |
| ``` | |
| --- | |
| ## Core Features | |
| ### 1. **Authentication** | |
| ```typescript | |
| // Login with credentials | |
| import { authService } from './services/authService'; | |
| const login = async (username: string, password: string) => { | |
| try { | |
| const response = await authService.login(username, password); | |
| await AsyncStorage.setItem('authToken', response.token); | |
| dispatch(setAuth(response.user)); | |
| } catch (error) { | |
| console.error('Login failed:', error); | |
| } | |
| }; | |
| // Biometric login (iOS/Android) | |
| import * as LocalAuthentication from 'react-native-local-authentication'; | |
| const biometricLogin = async () => { | |
| try { | |
| const compatible = await LocalAuthentication.hasHardwareAsync(); | |
| if (compatible) { | |
| await LocalAuthentication.authenticateAsync({ | |
| disableDeviceFallback: false, | |
| }); | |
| // Load token from secure storage | |
| } | |
| } catch (error) { | |
| console.error('Biometric auth failed:', error); | |
| } | |
| }; | |
| ``` | |
| ### 2. **Real-time Chat** | |
| ```typescript | |
| // Send message | |
| import { chatService } from './services/chatService'; | |
| const sendMessage = async (text: string) => { | |
| try { | |
| const message = { | |
| id: Date.now(), | |
| text, | |
| sender: 'user', | |
| timestamp: new Date(), | |
| synced: false, | |
| }; | |
| // Save locally first | |
| dispatch(addMessage(message)); | |
| // Sync when online | |
| if (isOnline) { | |
| const response = await chatService.sendMessage(text); | |
| dispatch(updateMessage({ id: message.id, ...response })); | |
| } | |
| } catch (error) { | |
| console.error('Message send failed:', error); | |
| } | |
| }; | |
| // Listen for push notifications | |
| messaging().onMessage(async (remoteMessage) => { | |
| console.log('Notification received:', remoteMessage); | |
| dispatch(addNotification(remoteMessage)); | |
| }); | |
| ``` | |
| ### 3. **Offline-First Sync** | |
| ```typescript | |
| // Redux Persist for offline support | |
| import { persistStore, persistReducer } from 'redux-persist'; | |
| import AsyncStorage from '@react-native-async-storage/async-storage'; | |
| const persistConfig = { | |
| key: 'root', | |
| storage: AsyncStorage, | |
| whitelist: ['chat', 'user', 'patients'], | |
| }; | |
| const persistedReducer = persistReducer(persistConfig, rootReducer); | |
| // Sync when connection restored | |
| NetInfo.addEventListener(state => { | |
| if (state.isConnected && hasPendingChanges) { | |
| syncWithServer(); | |
| } | |
| }); | |
| ``` | |
| ### 4. **Care Plan Management** | |
| ```typescript | |
| // View patient care plans | |
| import { carePlanService } from './services/carePlanService'; | |
| const loadCarePlans = async (patientId: string) => { | |
| try { | |
| let plans = await AsyncStorage.getItem(`carePlans_${patientId}`); | |
| if (!plans || isStale(plans)) { | |
| plans = await carePlanService.getCarePlans(patientId); | |
| await AsyncStorage.setItem( | |
| `carePlans_${patientId}`, | |
| JSON.stringify(plans) | |
| ); | |
| } | |
| dispatch(setCarePlans(JSON.parse(plans))); | |
| } catch (error) { | |
| console.error('Load care plans failed:', error); | |
| } | |
| }; | |
| ``` | |
| ### 5. **Health Indicators** | |
| ```typescript | |
| // Track vital signs | |
| import { healthService } from './services/healthService'; | |
| const recordVitals = async (vitals: VitalsData) => { | |
| try { | |
| const observation = { | |
| bloodPressure: vitals.bp, | |
| temperature: vitals.temp, | |
| oxygenSaturation: vitals.o2, | |
| heartRate: vitals.hr, | |
| recordedAt: new Date(), | |
| synced: false, | |
| }; | |
| // Save locally | |
| await AsyncStorage.setItem( | |
| 'lastVitals', | |
| JSON.stringify(observation) | |
| ); | |
| // Sync to server | |
| if (isOnline) { | |
| await healthService.recordObservation(observation); | |
| } | |
| } catch (error) { | |
| console.error('Record vitals failed:', error); | |
| } | |
| }; | |
| ``` | |
| --- | |
| ## Project Structure | |
| ``` | |
| mobile/ | |
| βββ App.tsx # Main app entry | |
| βββ package.json # Dependencies | |
| βββ tsconfig.json # TypeScript config | |
| βββ babel.config.js # Babel config | |
| βββ app.json # App metadata | |
| βββ index.js # React Native entry | |
| β | |
| βββ src/ | |
| β βββ screens/ | |
| β β βββ LoginScreen.tsx # Authentication | |
| β β βββ ChatScreen.tsx # Chat interface | |
| β β βββ CarePlanScreen.tsx # Care plan view | |
| β β βββ ProblemsScreen.tsx # Problems/diagnoses | |
| β β βββ InterventionsScreen.tsx # Interventions | |
| β β βββ HealthIndicatorsScreen.tsx | |
| β β βββ SettingsScreen.tsx # User settings | |
| β β βββ PatientDetailsScreen.tsx # Patient info | |
| β β | |
| β βββ components/ | |
| β β βββ ChatBubble.tsx # Message component | |
| β β βββ CarePlanCard.tsx # Care plan card | |
| β β βββ HealthIndicator.tsx # Vital signs | |
| β β βββ LoadingSpinner.tsx # Loading UI | |
| β β βββ ErrorBoundary.tsx # Error handling | |
| β β | |
| β βββ services/ | |
| β β βββ authService.ts # Authentication | |
| β β βββ chatService.ts # Chat API | |
| β β βββ carePlanService.ts # Care plans | |
| β β βββ healthService.ts # Health data | |
| β β βββ syncService.ts # Offline sync | |
| β β βββ api.ts # HTTP client | |
| β β | |
| β βββ store/ | |
| β β βββ index.ts # Store setup | |
| β β βββ rootReducer.ts # Root reducer | |
| β β βββ slices/ | |
| β β β βββ authSlice.ts # Auth state | |
| β β β βββ chatSlice.ts # Chat state | |
| β β β βββ patientSlice.ts # Patient state | |
| β β β βββ uiSlice.ts # UI state | |
| β β βββ hooks.ts # Redux hooks | |
| β β | |
| β βββ types/ | |
| β β βββ index.ts # TypeScript types | |
| β β βββ auth.ts # Auth types | |
| β β βββ chat.ts # Chat types | |
| β β βββ patient.ts # Patient types | |
| β β | |
| β βββ utils/ | |
| β β βββ formatters.ts # Data formatting | |
| β β βββ validators.ts # Input validation | |
| β β βββ storage.ts # Local storage | |
| β β βββ networkStatus.ts # Network detection | |
| β β | |
| β βββ styles/ | |
| β β βββ colors.ts # Theme colors | |
| β β βββ fonts.ts # Typography | |
| β β βββ spacing.ts # Spacing scale | |
| β β | |
| β βββ config/ | |
| β βββ constants.ts # App constants | |
| β βββ environment.ts # Env vars | |
| β βββ logger.ts # Logging | |
| β | |
| βββ ios/ # iOS native code | |
| β βββ Podfile # iOS dependencies | |
| β βββ NursingValidator/ # iOS app files | |
| β | |
| βββ android/ # Android native code | |
| β βββ build.gradle # Android config | |
| β βββ app/ # Android app files | |
| β | |
| βββ __tests__/ | |
| βββ screens/ # Screen tests | |
| βββ components/ # Component tests | |
| βββ services/ # Service tests | |
| βββ store/ # Redux tests | |
| ``` | |
| --- | |
| ## Key Packages | |
| ### Navigation | |
| - `@react-navigation/native` - Navigation framework | |
| - `@react-navigation/bottom-tabs` - Tab navigator | |
| - `@react-navigation/stack` - Stack navigator | |
| ### State Management | |
| - `redux` - State container | |
| - `@reduxjs/toolkit` - Redux utilities | |
| - `redux-persist` - Persist state | |
| ### UI Components | |
| - `react-native-vector-icons` - Icons | |
| - `react-native-svg` - SVG support | |
| - `react-native-gesture-handler` - Gestures | |
| ### Firebase | |
| - `@react-native-firebase/app` - Firebase core | |
| - `@react-native-firebase/messaging` - Push notifications | |
| - `@react-native-firebase/analytics` - Analytics | |
| ### Storage | |
| - `@react-native-async-storage/async-storage` - Key-value storage | |
| - `react-native-sqlite-storage` - Local database | |
| ### Forms | |
| - `formik` - Form management | |
| - `yup` - Validation schema | |
| --- | |
| ## Push Notifications Setup | |
| ### Firebase Cloud Messaging (FCM) | |
| ```bash | |
| # 1. Create Firebase project | |
| # https://firebase.google.com/ | |
| # 2. Download google-services.json (Android) | |
| # Place in: android/app/google-services.json | |
| # 3. Download GoogleService-Info.plist (iOS) | |
| # Place in: ios/GoogleService-Info.plist | |
| # 4. Install React Native Firebase | |
| npm install @react-native-firebase/app @react-native-firebase/messaging | |
| # 5. Link native modules | |
| cd ios && pod install && cd .. | |
| ``` | |
| ### Push Notification Handler | |
| ```typescript | |
| import messaging from '@react-native-firebase/messaging'; | |
| // Handle foreground messages | |
| messaging().onMessage(async (remoteMessage) => { | |
| console.log('Foreground message:', remoteMessage); | |
| // Update UI with notification | |
| }); | |
| // Handle background/killed messages | |
| messaging().onNotificationOpenedApp((remoteMessage) => { | |
| console.log('App opened from notification:', remoteMessage); | |
| // Navigate to appropriate screen | |
| }); | |
| // Get initial notification (app closed) | |
| messaging() | |
| .getInitialNotification() | |
| .then((remoteMessage) => { | |
| if (remoteMessage) { | |
| console.log('App opened from quit state:', remoteMessage); | |
| } | |
| }); | |
| ``` | |
| --- | |
| ## Testing | |
| ### Unit Tests | |
| ```bash | |
| # Run tests | |
| npm test | |
| # With coverage | |
| npm test -- --coverage | |
| ``` | |
| ### E2E Tests | |
| ```bash | |
| # Detox setup | |
| npm install -g detox-cli | |
| detox build-framework-cache | |
| # Run E2E tests | |
| detox test e2e --configuration ios.sim.debug | |
| ``` | |
| ### Sample Test | |
| ```typescript | |
| import { render, screen, fireEvent } from '@testing-library/react-native'; | |
| import LoginScreen from '../screens/LoginScreen'; | |
| describe('LoginScreen', () => { | |
| it('renders login form', () => { | |
| render(<LoginScreen />); | |
| expect(screen.getByPlaceholderText('Username')).toBeTruthy(); | |
| }); | |
| it('submits login form', async () => { | |
| const { getByPlaceholderText, getByText } = render(<LoginScreen />); | |
| fireEvent.changeText(getByPlaceholderText('Username'), 'nurse'); | |
| fireEvent.changeText(getByPlaceholderText('Password'), 'nurse2025'); | |
| fireEvent.press(getByText('Login')); | |
| // Assert navigation or API call | |
| }); | |
| }); | |
| ``` | |
| --- | |
| ## Troubleshooting | |
| ### Build Errors | |
| ```bash | |
| # Clear cache and rebuild | |
| rm -rf node_modules | |
| rm -rf Pods | |
| npm install | |
| cd ios && pod install && cd .. | |
| npm run ios | |
| ``` | |
| ### Simulator Issues | |
| ```bash | |
| # Reset simulator | |
| xcrun simctl erase all | |
| # Rebuild and run | |
| npm run ios | |
| ``` | |
| ### Android Issues | |
| ```bash | |
| # Clear Gradle cache | |
| cd android && ./gradlew clean && cd .. | |
| # Rebuild | |
| npm run android | |
| ``` | |
| --- | |
| ## Backend API Integration | |
| ### API Endpoints Required | |
| ``` | |
| POST /api/v1/auth/login - User authentication | |
| POST /api/v1/auth/logout - User logout | |
| GET /api/v1/user/profile - Get user profile | |
| POST /api/v1/chat/send - Send message | |
| GET /api/v1/chat/history - Get messages | |
| GET /api/v1/patients - List patients | |
| GET /api/v1/patients/{id} - Get patient | |
| GET /api/v1/patients/{id}/plans - Get care plans | |
| GET /api/v1/health/vitals - Get vital signs | |
| POST /api/v1/health/vitals - Record vitals | |
| POST /api/v1/notifications/token - Register push token | |
| ``` | |
| ### API Client Example | |
| ```typescript | |
| // src/services/api.ts | |
| import axios from 'axios'; | |
| import AsyncStorage from '@react-native-async-storage/async-storage'; | |
| const api = axios.create({ | |
| baseURL: process.env.REACT_APP_API_URL, | |
| timeout: 10000, | |
| }); | |
| // Add auth token to requests | |
| api.interceptors.request.use(async (config) => { | |
| const token = await AsyncStorage.getItem('authToken'); | |
| if (token) { | |
| config.headers.Authorization = `Bearer ${token}`; | |
| } | |
| return config; | |
| }); | |
| // Handle token expiry | |
| api.interceptors.response.use( | |
| (response) => response, | |
| async (error) => { | |
| if (error.response?.status === 401) { | |
| // Token expired, redirect to login | |
| await AsyncStorage.removeItem('authToken'); | |
| // Navigation.navigate('Login'); | |
| } | |
| return Promise.reject(error); | |
| } | |
| ); | |
| export default api; | |
| ``` | |
| --- | |
| ## Performance Optimization | |
| ### Image Optimization | |
| ```typescript | |
| import FastImage from 'react-native-fast-image'; | |
| // Use FastImage instead of Image | |
| <FastImage | |
| source={{ uri: imageUrl, priority: FastImage.priority.normal }} | |
| style={{ width: 200, height: 200 }} | |
| resizeMode={FastImage.resizeMode.contain} | |
| /> | |
| ``` | |
| ### FlatList Performance | |
| ```typescript | |
| // Optimize list rendering | |
| <FlatList | |
| data={messages} | |
| renderItem={({ item }) => <ChatBubble {...item} />} | |
| keyExtractor={(item) => item.id} | |
| removeClippedSubviews={true} | |
| maxToRenderPerBatch={10} | |
| updateCellsBatchingPeriod={50} | |
| initialNumToRender={10} | |
| onEndReachedThreshold={0.5} | |
| onEndReached={loadMoreMessages} | |
| /> | |
| ``` | |
| ### Code Splitting | |
| ```typescript | |
| // Lazy load screens | |
| const CarePlanScreen = React.lazy(() => import('./CarePlanScreen')); | |
| const ProblemsScreen = React.lazy(() => import('./ProblemsScreen')); | |
| ``` | |
| --- | |
| ## Deployment | |
| ### iOS App Store | |
| ```bash | |
| # Build release | |
| npm run build:ios | |
| # Submit to App Store Connect | |
| # https://appstoreconnect.apple.com | |
| ``` | |
| ### Google Play Store | |
| ```bash | |
| # Build release APK | |
| npm run build:android | |
| # Upload to Google Play Console | |
| # https://play.google.com/console | |
| ``` | |
| --- | |
| ## Next Steps | |
| 1. Set up development environment (Node.js, Xcode, Android Studio) | |
| 2. Clone mobile project | |
| 3. Configure backend connection | |
| 4. Run on simulator/device | |
| 5. Implement push notifications | |
| 6. Add biometric authentication | |
| 7. Test offline sync | |
| 8. Deploy to app stores | |
| --- | |
| ## Files Provided | |
| **New Files:** | |
| - `mobile/package.json` - Dependencies | |
| - `mobile/App.tsx` - Main app component | |
| - `mobile/src/screens/` - Screen components (skeleton) | |
| - `mobile/src/services/` - API services (skeleton) | |
| - `mobile/src/store/` - Redux state (skeleton) | |
| **Documentation:** | |
| - `PHASE2_MOBILE.md` (this file) | |
| --- | |
| ## References | |
| - [React Native Docs](https://reactnative.dev/) | |
| - [React Navigation](https://reactnavigation.org/) | |
| - [Redux Toolkit](https://redux-toolkit.js.org/) | |
| - [Firebase React Native](https://rnfirebase.io/) | |
| - [React Native Firebase Messaging](https://rnfirebase.io/messaging/overview) | |
| --- | |
| *Phase 2.4 Mobile App - November 29, 2025* | |