A production-ready React Native Expo mobile application demonstrating advanced mobile development practices, native features integration, and comprehensive security implementation.
Built for the React Native Developer Assignment - showcasing expertise in authentication, WebView integration, state management, native features, and real-world mobile app challenges.
Watch the complete app walkthrough (3-5 minutes):
πΊ Click to watch on YouTube
What's covered:
- Authentication & User Registration
- Course Discovery & Browsing
- Bookmarks & Enrollment
- User Profile Management
- Dark Mode & Real-time Features
Intro Screen β’ Registration β’ Login
Home β’ Course Catalog β’ Bookmarks
User Profile β’ Course Preview β’ Course Details
Ready to test? Download the production APK:
Note: You may need to enable "Install from Unknown Sources" in your Android settings to install the APK.
- Node.js 18+ and npm
- Expo CLI - Install globally:
npm install -g expo-cli - iOS Simulator (macOS only) or Android Studio with emulator
- Git for version control
-
Clone the repository
git clone <repository-url> cd mlms
-
Install dependencies
npm install
-
Configure environment variables
# Copy the example environment file cp .env.example .env # Edit .env with your configuration (see Environment Variables section below)
-
Start the development server
npm start
-
Run on device/emulator
# iOS (macOS only) npm run ios # Android npm run android # Web (limited native features) npm run web
# Install EAS CLI
npm install -g eas-cli
# Login to Expo account
eas login
# Configure build
eas build:configure
# Build development APK
eas build --platform android --profile development
# Build production APK
eas build --platform android --profile production# Run all tests
npm test
# Run tests with coverage report
npm run test:coverage
# Run TypeScript type checking
npm run typecheck
# Run linter
npm run lintCreate a .env file in the project root with the following variables:
# API Configuration
EXPO_PUBLIC_API_BASE_URL=https://api.freeapi.app/api/v1
EXPO_PUBLIC_API_TIMEOUT=60000
# Environment
EXPO_PUBLIC_ENV=development| Variable | Description | Default | Required |
|---|---|---|---|
EXPO_PUBLIC_API_BASE_URL |
Base URL for FreeAPI backend | https://api.freeapi.app/api/v1 |
β Yes |
EXPO_PUBLIC_API_TIMEOUT |
API request timeout in milliseconds | 60000 (60s) |
β Yes |
EXPO_PUBLIC_ENV |
Environment mode (development, staging, production) |
development |
β Yes |
Note: All environment variables must be prefixed with EXPO_PUBLIC_ to be accessible in the Expo app.
Choice: Zustand + Expo SecureStore + AsyncStorage
- Zustand: Lightweight global state management with TypeScript support and minimal boilerplate
- Expo SecureStore: Platform-encrypted storage for sensitive data (auth tokens) using iOS Keychain and Android Keystore
- AsyncStorage: Non-sensitive data persistence (bookmarks, preferences, cache)
Why not Redux? Zustand offers better TypeScript inference, smaller bundle size, and simpler API while maintaining powerful features like middleware and DevTools integration.
Centralized Axios Client with Interceptors
// Request Interceptor: Auto token injection
apiClient.interceptors.request.use(async (config) => {
const token = await getAccessToken();
if (token) config.headers.Authorization = `Bearer ${token}`;
return config;
});
// Response Interceptor: Auto token refresh on 401
apiClient.interceptors.response.use(
(response) => response,
async (error) => {
if (error.response?.status === 401) {
// Attempt token refresh and retry request
}
return Promise.reject(error);
}
);Features:
- Automatic auth token injection on every request
- Token refresh handling on 401 responses
- Exponential backoff retry logic (3 attempts) for network/5xx errors
- Request deduplication to prevent duplicate concurrent requests
- 60-second timeout with graceful error handling
File-based Routing with Expo Router
app/
βββ _layout.tsx # Root layout with providers
βββ (auth)/ # Unauthenticated routes
β βββ index.tsx # Welcome screen
β βββ login.tsx # Login
β βββ register.tsx # Register
βββ (app)/ # Authenticated routes (protected)
βββ (tabs)/ # Bottom tab navigation
β βββ index.tsx # Courses
β βββ bookmarks.tsx # Bookmarks
β βββ profile.tsx # Profile
βββ course/[id].tsx # Dynamic course details
βββ webview.tsx # WebView content viewer
Benefits:
- Type-safe navigation with automatic TypeScript types
- Automatic deep linking support
- Cleaner code organization with convention over configuration
- Built-in authentication guards via layout groups
List Rendering:
FlashList(drop-in FlatList replacement) for 10x better performanceReact.memoon CourseCard components to prevent unnecessary re-renderskeyExtractorusing stable IDsremoveClippedSubviewsfor off-screen view recycling- Pagination with infinite scroll (20 items per page)
Component Optimization:
useCallbackfor stable function references in propsuseMemofor expensive computations (search filtering, sorting)- Expo Image with built-in caching and progressive loading
- Code splitting with dynamic imports where applicable
Multi-layered Security Approach:
-
Secure Token Storage
- iOS: Keychain Services (hardware-encrypted)
- Android: Keystore System (hardware-backed if available)
- Web: Encrypted localStorage with fallback
-
Data Encryption
- AES-256 encryption for sensitive user data
- SHA-256 hashing for password validation
- Secure random key generation using Expo Crypto
-
Input Validation
- Zod schemas for runtime type validation
- XSS protection via input sanitization
- Email format validation and normalization
-
Network Security
- HTTPS-only API communication
- Certificate pinning support (configurable)
- Request timeout and retry limits
-
Device Security
- Jailbreak/root detection (iOS/Android)
- Secure flag for screenshots (Android)
Resilient Error Management:
// Error Boundary for React crashes
<ErrorBoundary fallback={<ErrorFallbackUI />}>
<App />
</ErrorBoundary>
// Network error handling with retry
try {
await fetchCourses();
} catch (error) {
if (isNetworkError(error)) {
showRetryOption();
} else {
showGenericError();
}
}
// Offline mode detection
useNetworkMonitor((isConnected) => {
if (!isConnected) {
showOfflineBanner();
}
});Features:
- React Error Boundary to catch component crashes
- Offline mode banner with real-time network monitoring
- Axios retry interceptor with exponential backoff
- User-friendly error messages mapped from API codes
- Graceful degradation for missing native features
Comprehensive Test Coverage (>70%)
__tests__/
βββ components/ # Component behavior tests
βββ services/ # API service tests
βββ stores/ # State management tests
βββ hooks/ # Custom hook tests
βββ utils/ # Utility function tests
Stack:
- Jest + React Native Testing Library
- Mock implementation for Expo modules
- Coverage enforcement via jest.config.js
- CI/CD integration ready
mlms/
βββ app/ # Expo Router file-based routing
β βββ _layout.tsx # Root layout (providers, theme)
β βββ global.css # NativeWind global styles
β βββ (app)/ # Authenticated routes
β β βββ _layout.tsx # App layout with tab navigation
β β βββ (tabs)/ # Bottom tabs
β β β βββ index.tsx # Courses home
β β β βββ bookmarks.tsx # Bookmarks
β β β βββ profile.tsx # Profile
β β βββ course/[id].tsx # Course detail (dynamic route)
β β βββ webview.tsx # WebView content viewer
β β βββ enrolled.tsx # Enrolled courses
β β βββ change-password.tsx # Password change
β βββ (auth)/ # Public routes
β βββ index.tsx # Welcome/Intro
β βββ login.tsx # Login
β βββ register.tsx # Registration
β βββ forgot-password.tsx # Password recovery
β
βββ src/
β βββ components/ # Reusable UI components
β β βββ ThemedButton.tsx
β β βββ ThemedInput.tsx
β β βββ CourseCard.tsx
β β βββ ErrorBoundary.tsx
β β βββ AppNotify/ # Notification system
β β βββ NetworkStatus/ # Network indicators
β β
β βββ screens/ # Screen components
β β βββ LoginScreen.tsx
β β βββ CourseListScreen.tsx
β β βββ CourseDetailScreen.tsx
β β βββ ...
β β
β βββ services/ # API and business logic
β β βββ api.ts # Axios client + interceptors
β β βββ auth.service.ts # Auth API calls
β β βββ course.service.ts # Course API calls
β β βββ notification.service.ts
β β
β βββ stores/ # Zustand state stores
β β βββ bookmarkStore.ts # Bookmarks management
β β βββ courseStore.ts # Courses cache
β β βββ networkStore.ts # Network state
β β
β βββ context/ # React Context providers
β β βββ auth-context.tsx # Auth state
β β βββ theme-context.tsx # Dark/light theme
β β βββ notification-context.tsx
β β
β βββ hooks/ # Custom React hooks
β β βββ useNetworkMonitor.ts
β β βββ useStorageState.ts
β β βββ useNotificationHandler.ts
β β
β βββ types/ # TypeScript definitions
β β βββ auth.types.ts # Auth + validation schemas
β β βββ course.types.ts # Course/product types
β β βββ api.types.ts # API request/response
β β
β βββ utils/ # Utility functions
β β βββ logger.utils.ts # Centralized logging
β β βββ storage.utils.ts # Secure storage helpers
β β βββ security.utils.ts # Encryption, validation
β β βββ error.utils.ts # Error mapping
β β βββ date.utils.ts # Date formatting
β β
β βββ constants/ # App-wide constants
β βββ app.constants.ts # App metadata, API config
β βββ colors.constants.ts # Theme colors
β βββ tabs.constants.ts # Tab configuration
β
βββ assets/ # Static assets
β βββ images/ # Icons, splash screens
β
βββ documentation/ # Project documentation
β βββ tasks.md # Assignment requirements
β βββ FreeAPI_*.md # API documentation
β βββ TESTING.md # Testing guide
β βββ SECURITY.md # Security practices
β βββ QUICK_REFERENCE.md # Quick commands
β
βββ .env.example # Environment template
βββ app.config.js # Expo configuration
βββ jest.config.js # Jest testing config
βββ tailwind.config.js # NativeWind config
βββ tsconfig.json # TypeScript config
| Category | Technology | Purpose |
|---|---|---|
| Framework | React Native Expo SDK 54 | Cross-platform mobile development |
| Language | TypeScript (strict mode) | Type safety and developer experience |
| Navigation | Expo Router | File-based routing with type safety |
| Styling | NativeWind (Tailwind CSS) | Utility-first responsive styling |
| State Management | Zustand | Lightweight global state |
| Data Fetching | Axios | HTTP client with interceptors |
| Forms | React Hook Form | Performant form handling |
| Validation | Zod | Runtime schema validation |
| Testing | Jest + Testing Library | Unit and integration testing |
| Secure Storage | Expo SecureStore | Encrypted token storage |
| Persistence | AsyncStorage | Non-sensitive data cache |
| Notifications | Expo Notifications | Push notifications |
| Images | Expo Image | Optimized image loading |
| Network | Expo Network | Connectivity monitoring |
| Encryption | Expo Crypto | AES encryption, SHA hashing |
- Login/Register with FreeAPI
/api/v1/usersendpoints - Secure token storage using Expo SecureStore (Keychain/Keystore)
- Auto-login on app restart with token validation
- Logout with complete token cleanup
- Token refresh handling on 401 responses
- Profile screen with user info and stats
- Profile picture update (camera + gallery)
- User statistics (courses enrolled, bookmarks count)
- Password change functionality
- Fetch courses from
/api/v1/public/randomproducts - Fetch instructors from
/api/v1/public/randomusers - Scrollable course list with FlashList optimization
- Course thumbnail, instructor, title, description display
- Bookmark icon with toggle functionality
- Pull-to-refresh with smooth animations
- Real-time search filtering (title, category, description, instructor)
- Course detail screen with complete information
- Enroll button with visual feedback
- Bookmark persistence with AsyncStorage
- WebView screen for course content display
- HTML template for course details rendering
- Native-to-WebView communication via headers
- JavaScript injection for dynamic data
- WebView error handling
- Notification permission request on first app launch
- Local notification on 5+ courses bookmarked
- 24-hour inactivity reminder notification
- Android notification channels configuration
- Notification scheduling and cancellation
- Zustand stores for bookmarks, courses, network state
- Expo SecureStore for auth tokens (encrypted)
- AsyncStorage for bookmarks and preferences
- FlashList with proper optimization (keyExtractor, memo)
- Pull-to-refresh without jank
- Component memoization and stable callbacks
- Image caching with Expo Image
- API failure retry with exponential backoff (3 attempts)
- User-friendly error messages
- Request timeout handling (60s)
- Offline mode banner with network monitoring
- WebView load error handling
- React Error Boundary for crash recovery
- Testing: Jest + Testing Library with >70% coverage
- Security: AES encryption, jailbreak detection, input sanitization
- Dark Mode: System-aware theme switching
- Form Validation: Zod schemas with real-time feedback
- Logging: Centralized logger with environment awareness
- TypeScript: Strict mode with comprehensive types
- Performance: Optimized lists, memoization, caching
-
Token Security
- iOS: Keychain Services (hardware-encrypted)
- Android: Keystore (hardware-backed encryption when available)
- Automatic token cleanup on logout/401
-
Data Encryption
- AES-256 encryption for sensitive data
- SHA-256 hashing for integrity checks
- Secure random key generation
-
Input Protection
- XSS prevention via sanitization
- Email validation and normalization
- Password strength requirements (min 6 chars)
-
Network Security
- HTTPS-only communication
- Request/response timeout limits
- Certificate pinning support (optional)
-
Device Security
- Jailbreak detection (iOS)
- Root detection (Android)
- Platform-specific security checks
# Run all tests
npm test
# Run with coverage
npm run test:coverage
# Watch mode
npm run test:watchTest Coverage:
- Unit tests: Services, stores, utilities, hooks
- Component tests: UI components behavior
- Integration tests: API calls, state updates
- Overall coverage: >70% (enforced)
Test Files:
src/
βββ components/__tests__/
βββ services/__tests__/
βββ stores/__tests__/
βββ hooks/__tests__/
βββ utils/__tests__/
See TESTING.md for detailed testing guide.
Base URL: https://api.freeapi.app/api/v1
| Method | Endpoint | Description |
|---|---|---|
| POST | /users/register |
Register new user |
| POST | /users/login |
Login user |
| POST | /users/logout |
Logout current user |
| GET | /users/current-user |
Get current user profile |
| POST | /users/change-password |
Change user password |
| POST | /users/refresh-token |
Refresh access token |
| Method | Endpoint | Description |
|---|---|---|
| GET | /public/randomproducts |
Get random products (courses) |
| GET | /public/randomproducts?page=1&limit=20 |
Paginated products |
| GET | /public/randomusers |
Get random users (instructors) |
See documentation/FreeAPI_*.md for detailed API documentation.
-
Notifications on Simulators
- Issue: Push notifications don't work on iOS Simulator or Android Emulator
- Workaround: Test on physical device or use Expo Go
- Impact: Local notifications for bookmarks and inactivity reminders
-
Camera/Image Picker in Expo Go
- Issue: Full camera access requires development build
- Workaround: Use
expo-dev-clientor build custom APK with EAS - Impact: Profile picture upload from camera
-
Web Platform
- Issue: Limited native features (notifications, secure storage)
- Workaround: Graceful degradation with fallbacks
- Impact: Reduced functionality on web platform
-
Random Product/User Data
- Issue: FreeAPI returns random products/users, not real LMS data
- Mapping: Products β Courses, Users β Instructors
- Impact: No real course content, enrollment is local-only
-
No Course Content Backend
- Issue: WebView displays generated HTML, not real course videos/materials
- Workaround: Local HTML template with course details
- Impact: Limited WebView functionality demonstration
-
Token Refresh Reliability
- Issue: FreeAPI token refresh occasionally fails
- Workaround: Automatic re-login prompt on failure
- Impact: User may need to login again after extended session
-
First Launch Delay
- Issue: Initial API calls may be slow (~2-3s)
- Cause: FreeAPI cold start time
- Workaround: Loading indicators and skeleton screens
-
Large Image Loading
- Issue: Some product images are large (>1MB)
- Workaround: Expo Image with progressive loading and caching
- Impact: Minor delay on slow networks
- Expo Module Mocking
- Issue: Some Expo modules (SecureStore, Notifications) need manual mocks
- Workaround: Jest mock implementations in
jest.setup.js - Impact: Some integration tests may not cover full native behavior
- Implement proper course video playback
- Add biometric authentication (Face ID/Touch ID/Fingerprint)
- Integrate real error tracking (Sentry)
- Add analytics tracking (Expo Analytics)
- Implement background fetch for course updates
- Add offline course caching with sync
- Implement deep linking for course sharing
- Add accessibility features (screen reader support)
This project fulfills all mandatory requirements:
β Mandatory Technologies:
- React Native Expo (SDK 54)
- TypeScript (strict mode)
- Expo SecureStore (auth tokens)
- AsyncStorage (bookmarks)
- Expo Router (navigation)
- NativeWind (styling)
β Bonus Technologies:
- Jest + Testing Library (>70% coverage)
- React Hook Form (forms)
- Zod (validation)
- Expo Image (caching)
- Custom error tracking
- Custom logging solution
β All Parts Implemented:
- Part 1: Authentication & User Management β
- Part 2: Course Catalog β
- Part 3: WebView Integration β
- Part 4: Native Features β
- Part 5: State Management & Performance β
- Part 6: Error Handling β
- QUICK_REFERENCE.md - Common commands and patterns
- TESTING.md - Testing guide and best practices
- SECURITY.md - Security implementation details
- FreeAPI Documentation - API endpoint documentation
- Assignment Requirements - Original assignment spec
This project is built as part of a developer assignment and is for demonstration purposes.
Built with β€οΈ for the React Native Expo Developer Assignment
- FreeAPI for providing the backend API
- Expo for the amazing development framework
- React Native community for excellent documentation
- NativeWind for Tailwind CSS in React Native








