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
# 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
# Navigate to mobile directory
cd mobile
# Install dependencies
npm install
# Install Pods (iOS)
cd ios && pod install && cd ..
3. Configure Backend Connection
# 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
# 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
// 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
// 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
// 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
// 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
// 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 utilitiesredux-persist- Persist state
UI Components
react-native-vector-icons- Iconsreact-native-svg- SVG supportreact-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 storagereact-native-sqlite-storage- Local database
Forms
formik- Form managementyup- Validation schema
Push Notifications Setup
Firebase Cloud Messaging (FCM)
# 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
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
# Run tests
npm test
# With coverage
npm test -- --coverage
E2E Tests
# Detox setup
npm install -g detox-cli
detox build-framework-cache
# Run E2E tests
detox test e2e --configuration ios.sim.debug
Sample Test
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
# Clear cache and rebuild
rm -rf node_modules
rm -rf Pods
npm install
cd ios && pod install && cd ..
npm run ios
Simulator Issues
# Reset simulator
xcrun simctl erase all
# Rebuild and run
npm run ios
Android Issues
# 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
// 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
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
// 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
// Lazy load screens
const CarePlanScreen = React.lazy(() => import('./CarePlanScreen'));
const ProblemsScreen = React.lazy(() => import('./ProblemsScreen'));
Deployment
iOS App Store
# Build release
npm run build:ios
# Submit to App Store Connect
# https://appstoreconnect.apple.com
Google Play Store
# Build release APK
npm run build:android
# Upload to Google Play Console
# https://play.google.com/console
Next Steps
- Set up development environment (Node.js, Xcode, Android Studio)
- Clone mobile project
- Configure backend connection
- Run on simulator/device
- Implement push notifications
- Add biometric authentication
- Test offline sync
- Deploy to app stores
Files Provided
New Files:
mobile/package.json- Dependenciesmobile/App.tsx- Main app componentmobile/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
- React Navigation
- Redux Toolkit
- Firebase React Native
- React Native Firebase Messaging
Phase 2.4 Mobile App - November 29, 2025