React Native
Mobile Platform
React Native
Overview
React Native is a cross-platform mobile development framework developed by Meta (formerly Facebook) that enables native app development using React's declarative UI paradigm and JavaScript/TypeScript. Following the philosophy of "Learn Once, Write Anywhere," it provides true native performance for both iOS and Android platforms while offering a familiar development experience for web developers. As of 2025, with the stabilization of the New Architecture (TurboModules + Fabric), complete integration with React 18, TypeScript-first development experience, and enhanced performance optimizations, React Native has evolved as a leading platform for enterprise-grade application development.
Details
React Native 2025 edition achieves breakthrough performance through the complete stabilization of the New Architecture, breaking free from traditional Bridge-based limitations with JSI (JavaScript Interface) for direct native module communication and the Fabric renderer for high-speed UI updates. Particularly noteworthy is the deep integration with React 18's Concurrent Features, enabling full utilization of modern React capabilities like Suspense, Transitions, and automatic batching in mobile applications. With Metro bundler acceleration, enhanced Flipper integration, Expo Dev Tools collaboration, and automatic type generation through Codegen, it provides a comprehensive development platform that balances development efficiency with application quality.
Key Features
- New Architecture: High-speed rendering with TurboModules + Fabric
- React 18 Integration: Support for Concurrent Features, Suspense, and Transitions
- TypeScript First: Complete type safety and excellent development experience
- Hot Reloading: Instant code change reflection and debugging efficiency
- Native Modules: Direct access to iOS and Android-specific functionality
- Rich Ecosystem: Extensive libraries and community support
Latest 2025 Features
- New Architecture Stable: Full production deployment of TurboModules, Fabric, and JSI
- React 18 Full Support: Concurrent Features and Automatic Batching support
- TypeScript 5.0 Support: Latest type system and development tools integration
- Performance Optimizations: 50% startup time reduction and memory usage optimization
- Enhanced Developer Experience: Flipper v2, Metro improvements, enhanced debugging
- Web Platform Alignment: Enhanced integration with React Native Web
Advantages and Disadvantages
Advantages
- Low learning curve for web developers with React-based development
- High code sharing rate between iOS and Android (70-90%)
- True native performance and UI experience
- Extensive third-party libraries and community support
- Fast development cycles with Hot Reloading
- Continuous development by Meta and proven enterprise track record
- Enhanced development efficiency through Expo integration
Disadvantages
- Complexity management challenges in large-scale applications
- Learning cost and compatibility issues with New Architecture migration
- Platform-specific knowledge required for native module development
- Larger app size compared to pure native development
- Limitations in perfectly replicating platform-specific UI
- Debugging complexity (JavaScript, Native Bridge, native code)
Reference Pages
Code Examples
Setup and Basic Configuration
# Install React Native CLI
npm install -g react-native-cli
# Create new project
npx react-native init MyAwesomeApp --template react-native-template-typescript
# Navigate to project directory
cd MyAwesomeApp
# Install dependencies
npm install
# Install CocoaPods dependencies for iOS (macOS only)
cd ios && pod install && cd ..
# Run on Android emulator
npx react-native run-android
# Run on iOS simulator (macOS only)
npx react-native run-ios
# Start Metro bundler manually
npx react-native start
# Install essential libraries for new project
npm install @react-navigation/native @react-navigation/stack @react-navigation/bottom-tabs
npm install react-native-screens react-native-safe-area-context
npm install react-native-gesture-handler react-native-reanimated
npm install @reduxjs/toolkit react-redux
npm install @react-native-async-storage/async-storage
// App.tsx - Main Application Component
import React from 'react';
import { NavigationContainer } from '@react-navigation/native';
import { createStackNavigator } from '@react-navigation/stack';
import { Provider } from 'react-redux';
import { PersistGate } from 'redux-persist/integration/react';
import { SafeAreaProvider } from 'react-native-safe-area-context';
import { store, persistor } from './src/store/store';
import HomeScreen from './src/screens/HomeScreen';
import ProfileScreen from './src/screens/ProfileScreen';
import LoadingScreen from './src/components/LoadingScreen';
export type RootStackParamList = {
Home: undefined;
Profile: { userId: string };
Settings: undefined;
};
const Stack = createStackNavigator<RootStackParamList>();
const App: React.FC = () => {
return (
<Provider store={store}>
<PersistGate loading={<LoadingScreen />} persistor={persistor}>
<SafeAreaProvider>
<NavigationContainer>
<Stack.Navigator
initialRouteName="Home"
screenOptions={{
headerStyle: {
backgroundColor: '#6200EE',
},
headerTintColor: '#fff',
headerTitleStyle: {
fontWeight: 'bold',
},
}}
>
<Stack.Screen
name="Home"
component={HomeScreen}
options={{ title: 'Home' }}
/>
<Stack.Screen
name="Profile"
component={ProfileScreen}
options={({ route }) => ({
title: `Profile: ${route.params.userId}`
})}
/>
</Stack.Navigator>
</NavigationContainer>
</SafeAreaProvider>
</PersistGate>
</Provider>
);
};
export default App;
Authentication and User Management
// src/services/AuthService.ts - Authentication Service
import AsyncStorage from '@react-native-async-storage/async-storage';
import { GoogleSignin, statusCodes } from '@react-native-google-signin/google-signin';
import { LoginManager, AccessToken } from 'react-native-fbsdk-next';
import auth, { FirebaseAuthTypes } from '@react-native-firebase/auth';
export interface User {
id: string;
email: string;
name: string;
avatar?: string;
provider: 'email' | 'google' | 'facebook' | 'apple';
emailVerified: boolean;
createdAt: Date;
lastLoginAt: Date;
}
export interface LoginCredentials {
email: string;
password: string;
}
export interface RegisterCredentials extends LoginCredentials {
name: string;
confirmPassword: string;
}
class AuthService {
private static instance: AuthService;
private currentUser: User | null = null;
private constructor() {
this.initializeGoogleSignIn();
this.setupAuthStateListener();
}
public static getInstance(): AuthService {
if (!AuthService.instance) {
AuthService.instance = new AuthService();
}
return AuthService.instance;
}
// Initialize Google Sign-In
private initializeGoogleSignIn(): void {
GoogleSignin.configure({
webClientId: 'your-web-client-id.googleusercontent.com',
offlineAccess: true,
hostedDomain: '',
forceCodeForRefreshToken: true,
});
}
// Setup authentication state listener
private setupAuthStateListener(): void {
auth().onAuthStateChanged(async (firebaseUser) => {
if (firebaseUser) {
await this.handleUserSession(firebaseUser);
} else {
this.currentUser = null;
await AsyncStorage.removeItem('user_token');
}
});
}
// Handle user session
private async handleUserSession(firebaseUser: FirebaseAuthTypes.User): Promise<void> {
try {
const idToken = await firebaseUser.getIdToken();
await AsyncStorage.setItem('user_token', idToken);
this.currentUser = {
id: firebaseUser.uid,
email: firebaseUser.email!,
name: firebaseUser.displayName || 'Unknown User',
avatar: firebaseUser.photoURL || undefined,
provider: this.getAuthProvider(firebaseUser),
emailVerified: firebaseUser.emailVerified,
createdAt: firebaseUser.metadata.creationTime ? new Date(firebaseUser.metadata.creationTime) : new Date(),
lastLoginAt: firebaseUser.metadata.lastSignInTime ? new Date(firebaseUser.metadata.lastSignInTime) : new Date(),
};
} catch (error) {
console.error('Handle user session error:', error);
}
}
// Determine auth provider
private getAuthProvider(user: FirebaseAuthTypes.User): User['provider'] {
const providerId = user.providerData[0]?.providerId;
switch (providerId) {
case 'google.com':
return 'google';
case 'facebook.com':
return 'facebook';
case 'apple.com':
return 'apple';
default:
return 'email';
}
}
// Email/Password registration
async registerWithEmail(credentials: RegisterCredentials): Promise<User> {
try {
if (credentials.password !== credentials.confirmPassword) {
throw new Error('Passwords do not match');
}
if (credentials.password.length < 8) {
throw new Error('Password must be at least 8 characters long');
}
const userCredential = await auth().createUserWithEmailAndPassword(
credentials.email,
credentials.password
);
await userCredential.user.updateProfile({
displayName: credentials.name,
});
await userCredential.user.sendEmailVerification();
return this.currentUser!;
} catch (error: any) {
console.error('Register error:', error);
throw new Error(this.getFirebaseErrorMessage(error.code));
}
}
// Email/Password login
async loginWithEmail(credentials: LoginCredentials): Promise<User> {
try {
await auth().signInWithEmailAndPassword(credentials.email, credentials.password);
return this.currentUser!;
} catch (error: any) {
console.error('Login error:', error);
throw new Error(this.getFirebaseErrorMessage(error.code));
}
}
// Google login
async loginWithGoogle(): Promise<User> {
try {
await GoogleSignin.hasPlayServices();
const { idToken } = await GoogleSignin.signIn();
const googleCredential = auth.GoogleAuthProvider.credential(idToken);
await auth().signInWithCredential(googleCredential);
return this.currentUser!;
} catch (error: any) {
console.error('Google login error:', error);
if (error.code === statusCodes.SIGN_IN_CANCELLED) {
throw new Error('Login was cancelled');
} else if (error.code === statusCodes.IN_PROGRESS) {
throw new Error('Login is in progress');
} else if (error.code === statusCodes.PLAY_SERVICES_NOT_AVAILABLE) {
throw new Error('Google Play Services not available');
}
throw new Error('Google login failed');
}
}
// Facebook login
async loginWithFacebook(): Promise<User> {
try {
const result = await LoginManager.logInWithPermissions(['public_profile', 'email']);
if (result.isCancelled) {
throw new Error('Login was cancelled');
}
const data = await AccessToken.getCurrentAccessToken();
if (!data) {
throw new Error('Failed to get Facebook access token');
}
const facebookCredential = auth.FacebookAuthProvider.credential(data.accessToken);
await auth().signInWithCredential(facebookCredential);
return this.currentUser!;
} catch (error) {
console.error('Facebook login error:', error);
throw error;
}
}
// Password reset
async resetPassword(email: string): Promise<void> {
try {
await auth().sendPasswordResetEmail(email);
} catch (error: any) {
console.error('Password reset error:', error);
throw new Error(this.getFirebaseErrorMessage(error.code));
}
}
// Resend email verification
async resendEmailVerification(): Promise<void> {
try {
const user = auth().currentUser;
if (user && !user.emailVerified) {
await user.sendEmailVerification();
}
} catch (error) {
console.error('Resend email verification error:', error);
throw error;
}
}
// Logout
async logout(): Promise<void> {
try {
await GoogleSignin.signOut();
await LoginManager.logOut();
await auth().signOut();
this.currentUser = null;
await AsyncStorage.removeItem('user_token');
} catch (error) {
console.error('Logout error:', error);
throw error;
}
}
// Get current user
getCurrentUser(): User | null {
return this.currentUser;
}
// Check login status
isLoggedIn(): boolean {
return this.currentUser !== null && auth().currentUser !== null;
}
// Convert Firebase error messages
private getFirebaseErrorMessage(errorCode: string): string {
const errorMessages: { [key: string]: string } = {
'auth/user-not-found': 'User not found',
'auth/wrong-password': 'Incorrect password',
'auth/email-already-in-use': 'Email address is already in use',
'auth/weak-password': 'Password is too weak',
'auth/invalid-email': 'Invalid email address format',
'auth/too-many-requests': 'Too many requests. Please try again later',
'auth/network-request-failed': 'Network error occurred',
};
return errorMessages[errorCode] || 'Authentication error occurred';
}
}
export default AuthService.getInstance();
Backend Integration and API Access
// src/services/ApiService.ts - API Service
import AsyncStorage from '@react-native-async-storage/async-storage';
export interface ApiConfig {
baseURL: string;
timeout: number;
retryAttempts: number;
retryDelay: number;
}
export interface ApiResponse<T> {
data: T;
status: number;
message?: string;
errors?: string[];
}
export interface PaginatedResponse<T> {
data: T[];
pagination: {
page: number;
limit: number;
total: number;
pages: number;
};
}
class ApiService {
private config: ApiConfig;
private authToken: string | null = null;
constructor(config: ApiConfig) {
this.config = config;
this.loadAuthToken();
}
// Load authentication token
private async loadAuthToken(): Promise<void> {
try {
this.authToken = await AsyncStorage.getItem('user_token');
} catch (error) {
console.error('Load auth token error:', error);
}
}
// Set authentication token
public setAuthToken(token: string | null): void {
this.authToken = token;
}
// Build request headers
private getHeaders(): Record<string, string> {
const headers: Record<string, string> = {
'Content-Type': 'application/json',
'Accept': 'application/json',
};
if (this.authToken) {
headers['Authorization'] = `Bearer ${this.authToken}`;
}
return headers;
}
// Execute HTTP request
private async executeRequest<T>(
url: string,
options: RequestInit,
attempt: number = 1
): Promise<ApiResponse<T>> {
try {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), this.config.timeout);
const response = await fetch(`${this.config.baseURL}${url}`, {
...options,
headers: {
...this.getHeaders(),
...options.headers,
},
signal: controller.signal,
});
clearTimeout(timeoutId);
const responseData = await response.json();
if (!response.ok) {
throw new Error(responseData.message || `HTTP Error: ${response.status}`);
}
return {
data: responseData.data || responseData,
status: response.status,
message: responseData.message,
};
} catch (error: any) {
console.error(`API Request failed (attempt ${attempt}):`, error);
// Retry logic
if (attempt < this.config.retryAttempts && this.shouldRetry(error)) {
await this.delay(this.config.retryDelay * attempt);
return this.executeRequest<T>(url, options, attempt + 1);
}
throw error;
}
}
// Determine if retry should be attempted
private shouldRetry(error: any): boolean {
return (
error.name === 'AbortError' ||
error.message.includes('Network request failed') ||
error.message.includes('500') ||
error.message.includes('502') ||
error.message.includes('503')
);
}
// Delay utility
private delay(ms: number): Promise<void> {
return new Promise(resolve => setTimeout(resolve, ms));
}
// GET request
async get<T>(url: string, params?: Record<string, any>): Promise<ApiResponse<T>> {
let requestUrl = url;
if (params) {
const searchParams = new URLSearchParams();
Object.entries(params).forEach(([key, value]) => {
if (value !== undefined && value !== null) {
searchParams.append(key, String(value));
}
});
requestUrl += `?${searchParams.toString()}`;
}
return this.executeRequest<T>(requestUrl, {
method: 'GET',
});
}
// POST request
async post<T>(url: string, data?: any): Promise<ApiResponse<T>> {
return this.executeRequest<T>(url, {
method: 'POST',
body: data ? JSON.stringify(data) : undefined,
});
}
// PUT request
async put<T>(url: string, data?: any): Promise<ApiResponse<T>> {
return this.executeRequest<T>(url, {
method: 'PUT',
body: data ? JSON.stringify(data) : undefined,
});
}
// PATCH request
async patch<T>(url: string, data?: any): Promise<ApiResponse<T>> {
return this.executeRequest<T>(url, {
method: 'PATCH',
body: data ? JSON.stringify(data) : undefined,
});
}
// DELETE request
async delete<T>(url: string): Promise<ApiResponse<T>> {
return this.executeRequest<T>(url, {
method: 'DELETE',
});
}
// File upload
async uploadFile<T>(
url: string,
file: {
uri: string;
type: string;
name: string;
},
additionalData?: Record<string, string>
): Promise<ApiResponse<T>> {
const formData = new FormData();
formData.append('file', {
uri: file.uri,
type: file.type,
name: file.name,
} as any);
if (additionalData) {
Object.entries(additionalData).forEach(([key, value]) => {
formData.append(key, value);
});
}
const headers = this.getHeaders();
delete headers['Content-Type']; // Let the browser set the content-type for FormData
return this.executeRequest<T>(url, {
method: 'POST',
body: formData,
headers,
});
}
}
// Default configuration
const defaultConfig: ApiConfig = {
baseURL: __DEV__ ? 'http://localhost:3000/api' : 'https://api.yourapp.com',
timeout: 10000,
retryAttempts: 3,
retryDelay: 1000,
};
export const apiService = new ApiService(defaultConfig);
// Specific API endpoints
export interface Post {
id: string;
title: string;
content: string;
authorId: string;
authorName: string;
createdAt: string;
updatedAt: string;
likes: number;
comments: number;
tags: string[];
}
export interface User {
id: string;
email: string;
name: string;
avatar?: string;
bio?: string;
followers: number;
following: number;
}
export class PostApiService {
// Get posts with pagination
static async getPosts(page: number = 1, limit: number = 10): Promise<PaginatedResponse<Post>> {
const response = await apiService.get<PaginatedResponse<Post>>('/posts', {
page,
limit,
});
return response.data;
}
// Get post details
static async getPost(postId: string): Promise<Post> {
const response = await apiService.get<Post>(`/posts/${postId}`);
return response.data;
}
// Create post
static async createPost(postData: {
title: string;
content: string;
tags: string[];
}): Promise<Post> {
const response = await apiService.post<Post>('/posts', postData);
return response.data;
}
// Update post
static async updatePost(postId: string, updates: Partial<Post>): Promise<Post> {
const response = await apiService.put<Post>(`/posts/${postId}`, updates);
return response.data;
}
// Delete post
static async deletePost(postId: string): Promise<void> {
await apiService.delete(`/posts/${postId}`);
}
// Like post
static async likePost(postId: string): Promise<void> {
await apiService.post(`/posts/${postId}/like`);
}
// Unlike post
static async unlikePost(postId: string): Promise<void> {
await apiService.delete(`/posts/${postId}/like`);
}
// Search posts
static async searchPosts(query: string, tags?: string[]): Promise<Post[]> {
const response = await apiService.get<Post[]>('/posts/search', {
q: query,
tags: tags?.join(','),
});
return response.data;
}
}
export class UserApiService {
// Get user profile
static async getUserProfile(userId: string): Promise<User> {
const response = await apiService.get<User>(`/users/${userId}`);
return response.data;
}
// Update profile
static async updateProfile(updates: Partial<User>): Promise<User> {
const response = await apiService.put<User>('/users/profile', updates);
return response.data;
}
// Upload avatar image
static async uploadAvatar(imageUri: string): Promise<{ avatarUrl: string }> {
const response = await apiService.uploadFile<{ avatarUrl: string }>(
'/users/avatar',
{
uri: imageUri,
type: 'image/jpeg',
name: 'avatar.jpg',
}
);
return response.data;
}
// Follow user
static async followUser(userId: string): Promise<void> {
await apiService.post(`/users/${userId}/follow`);
}
// Unfollow user
static async unfollowUser(userId: string): Promise<void> {
await apiService.delete(`/users/${userId}/follow`);
}
// Get followers
static async getFollowers(userId: string): Promise<User[]> {
const response = await apiService.get<User[]>(`/users/${userId}/followers`);
return response.data;
}
// Get following users
static async getFollowing(userId: string): Promise<User[]> {
const response = await apiService.get<User[]>(`/users/${userId}/following`);
return response.data;
}
}
Push Notifications and Messaging
// src/services/NotificationService.ts - Push Notification Service
import PushNotification from 'react-native-push-notification';
import PushNotificationIOS from '@react-native-community/push-notification-ios';
import { Platform, PermissionsAndroid, Alert } from 'react-native';
import AsyncStorage from '@react-native-async-storage/async-storage';
import messaging from '@react-native-firebase/messaging';
export interface LocalNotification {
id?: number;
title: string;
message: string;
date?: Date;
data?: any;
actions?: string[];
category?: string;
}
export interface RemoteNotificationData {
title?: string;
body?: string;
data?: { [key: string]: string };
badge?: number;
sound?: string;
}
class NotificationService {
private static instance: NotificationService;
private isInitialized = false;
private fcmToken: string | null = null;
private constructor() {}
public static getInstance(): NotificationService {
if (!NotificationService.instance) {
NotificationService.instance = new NotificationService();
}
return NotificationService.instance;
}
// Initialize notification service
async initialize(): Promise<void> {
if (this.isInitialized) return;
try {
// Request permissions
await this.requestPermissions();
// Initialize FCM
await this.initializeFirebaseMessaging();
// Configure local notifications
this.configureLocalNotifications();
// Setup notification handlers
this.setupNotificationHandlers();
this.isInitialized = true;
console.log('Notification service initialized successfully');
} catch (error) {
console.error('Notification service initialization failed:', error);
throw error;
}
}
// Request permissions
private async requestPermissions(): Promise<void> {
try {
if (Platform.OS === 'ios') {
const authStatus = await messaging().requestPermission();
const enabled =
authStatus === messaging.AuthorizationStatus.AUTHORIZED ||
authStatus === messaging.AuthorizationStatus.PROVISIONAL;
if (!enabled) {
Alert.alert(
'Notification Permission',
'Please allow notifications in Settings to receive push notifications.',
[
{ text: 'Cancel', style: 'cancel' },
{ text: 'Open Settings', onPress: () => this.openAppSettings() },
]
);
}
} else if (Platform.OS === 'android') {
if (Platform.Version >= 33) {
const granted = await PermissionsAndroid.request(
PermissionsAndroid.PERMISSIONS.POST_NOTIFICATIONS
);
if (granted !== PermissionsAndroid.RESULTS.GRANTED) {
Alert.alert(
'Notification Permission',
'Please allow notifications to receive push notifications.'
);
}
}
}
} catch (error) {
console.error('Request permissions error:', error);
}
}
// Initialize Firebase Cloud Messaging
private async initializeFirebaseMessaging(): Promise<void> {
try {
// Get FCM token
this.fcmToken = await messaging().getToken();
console.log('FCM Token:', this.fcmToken);
// Register token with server
await this.registerToken(this.fcmToken);
// Token refresh listener
messaging().onTokenRefresh(async (token) => {
console.log('FCM Token refreshed:', token);
this.fcmToken = token;
await this.registerToken(token);
});
// Foreground message handler
messaging().onMessage(async (remoteMessage) => {
console.log('Foreground notification received:', remoteMessage);
this.handleForegroundNotification(remoteMessage);
});
// Background/quit state notification tap handler
messaging().onNotificationOpenedApp((remoteMessage) => {
console.log('Notification opened app:', remoteMessage);
this.handleNotificationAction(remoteMessage);
});
// Check for initial notification when app launches
const initialNotification = await messaging().getInitialNotification();
if (initialNotification) {
console.log('App opened from notification:', initialNotification);
this.handleNotificationAction(initialNotification);
}
} catch (error) {
console.error('Firebase messaging initialization error:', error);
}
}
// Configure local notifications
private configureLocalNotifications(): void {
PushNotification.configure({
// Notification received callback
onNotification: (notification) => {
console.log('Local notification received:', notification);
if (notification.userInteraction) {
this.handleLocalNotificationAction(notification);
}
// iOS completion notification
if (Platform.OS === 'ios') {
notification.finish(PushNotificationIOS.FetchResult.NoData);
}
},
// Registration error
onRegistrationError: (err) => {
console.error('Push notification registration error:', err);
},
// iOS permission request
requestPermissions: Platform.OS === 'ios',
// Notification action handling
onAction: (notification) => {
console.log('Notification action:', notification);
this.handleNotificationAction(notification);
},
// Notification channel setup (Android)
channelId: 'default-channel-id',
});
// Create Android notification channel
if (Platform.OS === 'android') {
PushNotification.createChannel(
{
channelId: 'default-channel-id',
channelName: 'Default Channel',
channelDescription: 'Default notification channel',
playSound: true,
soundName: 'default',
importance: 4,
vibrate: true,
},
(created) => console.log('Notification channel created:', created)
);
}
}
// Setup notification handlers
private setupNotificationHandlers(): void {
// App state change handling
if (Platform.OS === 'ios') {
PushNotificationIOS.addEventListener('notification', this.handleIOSNotification);
PushNotificationIOS.addEventListener('localNotification', this.handleIOSLocalNotification);
}
}
// iOS notification handler
private handleIOSNotification = (notification: any): void => {
console.log('iOS notification:', notification);
this.handleNotificationAction(notification);
};
// iOS local notification handler
private handleIOSLocalNotification = (notification: any): void => {
console.log('iOS local notification:', notification);
this.handleLocalNotificationAction(notification);
};
// Handle foreground notifications
private handleForegroundNotification(remoteMessage: any): void {
const { notification, data } = remoteMessage;
// Display as local notification even in foreground
PushNotification.localNotification({
title: notification?.title || 'New Message',
message: notification?.body || 'You have a new message',
playSound: true,
soundName: 'default',
userInfo: data,
channelId: 'default-channel-id',
});
}
// Handle notification actions
private handleNotificationAction(notification: any): void {
const { data } = notification;
// Screen navigation or action execution
if (data?.screen) {
console.log(`Navigate to screen: ${data.screen}`);
// Implement navigation logic here
}
if (data?.action) {
console.log(`Execute action: ${data.action}`);
// Implement action handling logic here
}
}
// Handle local notification actions
private handleLocalNotificationAction(notification: any): void {
console.log('Local notification action:', notification);
// Local notification specific handling
}
// Register FCM token with server
private async registerToken(token: string): Promise<void> {
try {
await AsyncStorage.setItem('fcm_token', token);
// Send token to API server
// await apiService.post('/users/fcm-token', { token });
console.log('FCM token registered successfully');
} catch (error) {
console.error('Register token error:', error);
}
}
// Send local notification
sendLocalNotification(notification: LocalNotification): void {
const notificationConfig: any = {
title: notification.title,
message: notification.message,
playSound: true,
soundName: 'default',
userInfo: notification.data,
channelId: 'default-channel-id',
};
if (notification.id) {
notificationConfig.id = notification.id;
}
if (notification.date) {
notificationConfig.date = notification.date;
PushNotification.localNotificationSchedule(notificationConfig);
} else {
PushNotification.localNotification(notificationConfig);
}
}
// Schedule notification
scheduleNotification(notification: LocalNotification, date: Date): void {
this.sendLocalNotification({
...notification,
date,
});
}
// Cancel notification
cancelNotification(notificationId: number): void {
PushNotification.cancelLocalNotifications({ id: notificationId.toString() });
}
// Cancel all notifications
cancelAllNotifications(): void {
PushNotification.cancelAllLocalNotifications();
}
// Clear badge (iOS)
clearBadge(): void {
if (Platform.OS === 'ios') {
PushNotificationIOS.setApplicationIconBadgeNumber(0);
}
}
// Set badge (iOS)
setBadge(count: number): void {
if (Platform.OS === 'ios') {
PushNotificationIOS.setApplicationIconBadgeNumber(count);
}
}
// Subscribe to topic
async subscribeToTopic(topic: string): Promise<void> {
try {
await messaging().subscribeToTopic(topic);
console.log(`Subscribed to topic: ${topic}`);
} catch (error) {
console.error('Subscribe to topic error:', error);
}
}
// Unsubscribe from topic
async unsubscribeFromTopic(topic: string): Promise<void> {
try {
await messaging().unsubscribeFromTopic(topic);
console.log(`Unsubscribed from topic: ${topic}`);
} catch (error) {
console.error('Unsubscribe from topic error:', error);
}
}
// Open app settings
private openAppSettings(): void {
if (Platform.OS === 'ios') {
PushNotificationIOS.requestPermissions();
} else {
// For Android, use react-native-app-settings or similar
}
}
// Get FCM token
getFCMToken(): string | null {
return this.fcmToken;
}
// Check notification permission status
async getPermissionStatus(): Promise<string> {
if (Platform.OS === 'ios') {
const authStatus = await messaging().hasPermission();
switch (authStatus) {
case messaging.AuthorizationStatus.AUTHORIZED:
return 'authorized';
case messaging.AuthorizationStatus.DENIED:
return 'denied';
case messaging.AuthorizationStatus.NOT_DETERMINED:
return 'not_determined';
case messaging.AuthorizationStatus.PROVISIONAL:
return 'provisional';
default:
return 'unknown';
}
} else {
return 'granted'; // Simplified for Android detailed status retrieval
}
}
}
export default NotificationService.getInstance();
Analytics and Performance Monitoring
// src/services/AnalyticsService.ts - Analytics Service
import analytics from '@react-native-firebase/analytics';
import crashlytics from '@react-native-firebase/crashlytics';
import perf from '@react-native-firebase/perf';
import { Platform } from 'react-native';
import DeviceInfo from 'react-native-device-info';
export interface AnalyticsEvent {
name: string;
parameters?: { [key: string]: any };
timestamp?: Date;
}
export interface UserProperties {
[key: string]: string | number | boolean;
}
export interface CustomMetrics {
[key: string]: number;
}
export interface PerformanceTrace {
name: string;
startTime: number;
metrics?: CustomMetrics;
attributes?: { [key: string]: string };
}
class AnalyticsService {
private static instance: AnalyticsService;
private activeTraces: Map<string, any> = new Map();
private sessionStartTime: number = Date.now();
private isInitialized = false;
private constructor() {}
public static getInstance(): AnalyticsService {
if (!AnalyticsService.instance) {
AnalyticsService.instance = new AnalyticsService();
}
return AnalyticsService.instance;
}
// Initialize analytics service
async initialize(): Promise<void> {
if (this.isInitialized) return;
try {
// Enable Analytics
await analytics().setAnalyticsCollectionEnabled(true);
// Enable Crashlytics
await crashlytics().setCrashlyticsCollectionEnabled(true);
// Set device properties
await this.setDeviceProperties();
// Track app start
await this.trackAppStart();
this.isInitialized = true;
console.log('Analytics service initialized successfully');
} catch (error) {
console.error('Analytics service initialization failed:', error);
this.recordError(error as Error);
}
}
// Set device information properties
private async setDeviceProperties(): Promise<void> {
try {
const deviceInfo = {
platform: Platform.OS,
version: Platform.Version.toString(),
device_model: await DeviceInfo.getModel(),
device_brand: await DeviceInfo.getBrand(),
system_version: await DeviceInfo.getSystemVersion(),
app_version: await DeviceInfo.getVersion(),
build_number: await DeviceInfo.getBuildNumber(),
is_tablet: await DeviceInfo.isTablet(),
total_memory: await DeviceInfo.getTotalMemory(),
};
// Set as user properties in Firebase Analytics
for (const [key, value] of Object.entries(deviceInfo)) {
await analytics().setUserProperty(key, String(value));
}
// Also set in Crashlytics
for (const [key, value] of Object.entries(deviceInfo)) {
crashlytics().setAttribute(key, String(value));
}
console.log('Device properties set:', deviceInfo);
} catch (error) {
console.error('Set device properties error:', error);
}
}
// Track app startup
private async trackAppStart(): Promise<void> {
try {
const startupTime = Date.now() - this.sessionStartTime;
await this.trackEvent('app_start', {
startup_time: startupTime,
timestamp: new Date().toISOString(),
session_id: this.generateSessionId(),
});
// Performance trace
const trace = perf().newTrace('app_startup');
trace.putMetric('startup_time_ms', startupTime);
await trace.start();
await trace.stop();
} catch (error) {
console.error('Track app start error:', error);
}
}
// Track custom events
async trackEvent(eventName: string, parameters?: { [key: string]: any }): Promise<void> {
try {
const eventParams = {
...parameters,
timestamp: new Date().toISOString(),
session_id: this.generateSessionId(),
};
await analytics().logEvent(eventName, eventParams);
console.log(`Event tracked: ${eventName}`, eventParams);
} catch (error) {
console.error('Track event error:', error);
this.recordError(error as Error);
}
}
// Track screen views
async trackScreenView(screenName: string, screenClass?: string, additionalData?: any): Promise<void> {
try {
await analytics().logScreenView({
screen_name: screenName,
screen_class: screenClass || screenName,
});
// Additional custom event
await this.trackEvent('screen_view', {
screen_name: screenName,
screen_class: screenClass || screenName,
...additionalData,
});
console.log(`Screen view tracked: ${screenName}`);
} catch (error) {
console.error('Track screen view error:', error);
}
}
// Set user properties
async setUserProperties(properties: UserProperties): Promise<void> {
try {
for (const [key, value] of Object.entries(properties)) {
await analytics().setUserProperty(key, String(value));
crashlytics().setAttribute(key, String(value));
}
console.log('User properties set:', properties);
} catch (error) {
console.error('Set user properties error:', error);
}
}
// Set user ID
async setUserId(userId: string): Promise<void> {
try {
await analytics().setUserId(userId);
await crashlytics().setUserId(userId);
console.log(`User ID set: ${userId}`);
} catch (error) {
console.error('Set user ID error:', error);
}
}
// Track e-commerce events
async trackPurchase(
transactionId: string,
value: number,
currency: string,
items: any[]
): Promise<void> {
try {
await analytics().logEvent('purchase', {
transaction_id: transactionId,
value,
currency,
items,
});
// Individual item events
for (const item of items) {
await analytics().logEvent('select_item', {
item_id: item.item_id,
item_name: item.item_name,
item_category: item.item_category,
value: item.price,
currency,
});
}
console.log('Purchase tracked:', { transactionId, value, currency });
} catch (error) {
console.error('Track purchase error:', error);
}
}
// Track user engagement
async trackUserEngagement(action: string, duration?: number, additionalData?: any): Promise<void> {
try {
const parameters: { [key: string]: any } = {
action,
...additionalData
};
if (duration !== undefined) {
parameters.engagement_time_msec = duration;
}
await analytics().logEvent('user_engagement', parameters);
console.log('User engagement tracked:', parameters);
} catch (error) {
console.error('Track user engagement error:', error);
}
}
// Start performance trace
async startPerformanceTrace(traceName: string): Promise<boolean> {
try {
const trace = perf().newTrace(traceName);
await trace.start();
this.activeTraces.set(traceName, {
trace,
startTime: Date.now(),
});
console.log(`Performance trace started: ${traceName}`);
return true;
} catch (error) {
console.error('Start performance trace error:', error);
return false;
}
}
// Stop performance trace
async stopPerformanceTrace(
traceName: string,
metrics?: CustomMetrics,
attributes?: { [key: string]: string }
): Promise<void> {
try {
const traceData = this.activeTraces.get(traceName);
if (!traceData) {
console.warn(`Performance trace not found: ${traceName}`);
return;
}
const { trace } = traceData;
const duration = Date.now() - traceData.startTime;
// Add metrics
if (metrics) {
for (const [key, value] of Object.entries(metrics)) {
trace.putMetric(key, value);
}
}
// Add attributes
if (attributes) {
for (const [key, value] of Object.entries(attributes)) {
trace.putAttribute(key, value);
}
}
// Duration metric
trace.putMetric('duration_ms', duration);
await trace.stop();
this.activeTraces.delete(traceName);
console.log(`Performance trace stopped: ${traceName}, duration: ${duration}ms`);
} catch (error) {
console.error('Stop performance trace error:', error);
}
}
// Measure async operations performance
async measureAsync<T>(traceName: string, fn: () => Promise<T>): Promise<T> {
await this.startPerformanceTrace(traceName);
try {
const result = await fn();
await this.stopPerformanceTrace(traceName, { success: 1 });
return result;
} catch (error) {
await this.stopPerformanceTrace(traceName, { success: 0, error: 1 });
throw error;
}
}
// Record errors (Crashlytics)
recordError(error: Error, additionalInfo?: { [key: string]: any }): void {
try {
if (additionalInfo) {
for (const [key, value] of Object.entries(additionalInfo)) {
crashlytics().setAttribute(key, String(value));
}
}
crashlytics().recordError(error);
console.log('Error recorded to Crashlytics:', error.message);
} catch (err) {
console.error('Record error to Crashlytics failed:', err);
}
}
// Custom logging
log(message: string, level: 'debug' | 'info' | 'warning' | 'error' = 'info'): void {
try {
crashlytics().log(`[${level.toUpperCase()}] ${message}`);
console.log(`Crashlytics log: ${message}`);
} catch (error) {
console.error('Crashlytics log error:', error);
}
}
// Set custom keys
setCustomKeys(keys: { [key: string]: any }): void {
try {
for (const [key, value] of Object.entries(keys)) {
crashlytics().setAttribute(key, String(value));
}
console.log('Custom keys set for crash reports:', keys);
} catch (error) {
console.error('Set custom keys error:', error);
}
}
// Track network requests
async trackNetworkRequest(
url: string,
method: string,
statusCode: number,
duration: number,
responseSize?: number
): Promise<void> {
try {
await this.trackEvent('network_request', {
url,
method,
status_code: statusCode,
duration_ms: duration,
response_size: responseSize || 0,
success: statusCode >= 200 && statusCode < 300 ? 1 : 0,
});
} catch (error) {
console.error('Track network request error:', error);
}
}
// Calculate session duration
getSessionDuration(): number {
return Date.now() - this.sessionStartTime;
}
// Generate session ID
private generateSessionId(): string {
return `session_${this.sessionStartTime}_${Math.random().toString(36).substr(2, 9)}`;
}
// Cleanup on app termination
async cleanup(): Promise<void> {
try {
// Stop active traces
for (const [traceName] of this.activeTraces) {
await this.stopPerformanceTrace(traceName);
}
// Track session end
await this.trackEvent('session_end', {
session_duration: this.getSessionDuration(),
});
console.log('Analytics service cleanup completed');
} catch (error) {
console.error('Analytics cleanup error:', error);
}
}
// Test crash (development only)
testCrash(): void {
if (__DEV__) {
crashlytics().crash();
}
}
}
export default AnalyticsService.getInstance();
// Convenient helper functions
export const performanceMonitor = {
// Measure API calls
async measureApiCall<T>(apiName: string, apiCall: () => Promise<T>): Promise<T> {
const analyticsService = AnalyticsService.getInstance();
return analyticsService.measureAsync(`api_${apiName}`, apiCall);
},
// Measure screen rendering time
async measureScreenRender(screenName: string, renderFunction: () => Promise<void>): Promise<void> {
const analyticsService = AnalyticsService.getInstance();
return analyticsService.measureAsync(`screen_render_${screenName}`, renderFunction);
},
// Measure database operations
async measureDatabaseOperation<T>(operation: string, dbCall: () => Promise<T>): Promise<T> {
const analyticsService = AnalyticsService.getInstance();
return analyticsService.measureAsync(`db_${operation}`, dbCall);
},
};
Deployment and Configuration
# metro.config.js - Metro bundler configuration
const {getDefaultConfig, mergeConfig} = require('@react-native/metro-config');
const defaultConfig = getDefaultConfig(__dirname);
const config = {
resolver: {
assetExts: [...defaultConfig.resolver.assetExts, 'bin'],
sourceExts: [...defaultConfig.resolver.sourceExts, 'svg'],
},
transformer: {
babelTransformerPath: require.resolve('react-native-svg-transformer'),
getTransformOptions: async () => ({
transform: {
experimentalImportSupport: false,
inlineRequires: true,
},
}),
},
watchFolders: [
// Add additional watch folders if needed
],
};
module.exports = mergeConfig(defaultConfig, config);
// package.json - Build & Deploy Scripts
{
"scripts": {
"android": "react-native run-android",
"ios": "react-native run-ios",
"start": "react-native start",
"test": "jest",
"lint": "eslint . --ext .js,.jsx,.ts,.tsx",
"build:android": "cd android && ./gradlew assembleRelease",
"build:android:bundle": "cd android && ./gradlew bundleRelease",
"build:ios": "cd ios && xcodebuild -workspace MyAwesomeApp.xcworkspace -scheme MyAwesomeApp -configuration Release -destination generic/platform=iOS -archivePath build/MyAwesomeApp.xcarchive archive",
"build:ios:simulator": "cd ios && xcodebuild -workspace MyAwesomeApp.xcworkspace -scheme MyAwesomeApp -configuration Debug -destination 'platform=iOS Simulator,name=iPhone 14' build",
"clean": "npx react-native-clean-project",
"clean:android": "cd android && ./gradlew clean",
"clean:ios": "cd ios && xcodebuild clean",
"postinstall": "cd ios && pod install",
"analyze:bundle": "npx react-native-bundle-visualizer",
"type-check": "tsc --noEmit",
"test:coverage": "jest --coverage",
"test:e2e": "detox test",
"test:e2e:build": "detox build",
"deploy:android": "cd android && ./gradlew assembleRelease && echo 'APK built successfully'",
"deploy:ios": "cd ios && xcodebuild -exportArchive -archivePath build/MyAwesomeApp.xcarchive -exportPath build -exportOptionsPlist ExportOptions.plist"
}
}
# release.sh - Release automation script
#!/bin/bash
# React Native Application Release Script
set -e
echo "🚀 Starting React Native Application Release"
# Get version information
VERSION=$(node -p "require('./package.json').version")
echo "📱 App Version: $VERSION"
# Environment check
echo "🔍 Checking environment..."
# Node.js version check
NODE_VERSION=$(node -v)
echo "Node.js Version: $NODE_VERSION"
# React Native CLI check
if ! command -v react-native &> /dev/null; then
echo "❌ React Native CLI is not installed"
exit 1
fi
# Install dependencies
echo "📦 Installing dependencies..."
npm ci
# TypeScript type check
echo "🔍 Running TypeScript type check..."
npm run type-check
# ESLint check
echo "🔍 Running ESLint check..."
npm run lint
# Run tests
echo "🧪 Running tests..."
npm run test
# Clear Metro bundler cache
echo "🧹 Clearing Metro cache..."
npx react-native start --reset-cache &
sleep 5
kill %1
# Android build
if [[ "$1" == "android" || "$1" == "both" ]]; then
echo "🤖 Starting Android build..."
# Android clean
cd android
./gradlew clean
# Release build
./gradlew assembleRelease
# APK size check
APK_SIZE=$(du -h app/build/outputs/apk/release/app-release.apk | cut -f1)
echo "📱 APK Size: $APK_SIZE"
# Move APK file
cp app/build/outputs/apk/release/app-release.apk ../builds/android/
cd ..
echo "✅ Android build completed"
fi
# iOS build (macOS only)
if [[ "$1" == "ios" || "$1" == "both" ]] && [[ "$OSTYPE" == "darwin"* ]]; then
echo "🍎 Starting iOS build..."
# Update CocoaPods
cd ios
pod install --repo-update
# Create archive
xcodebuild -workspace MyAwesomeApp.xcworkspace \
-scheme MyAwesomeApp \
-configuration Release \
-destination generic/platform=iOS \
-archivePath build/MyAwesomeApp.xcarchive \
archive
# Export IPA
xcodebuild -exportArchive \
-archivePath build/MyAwesomeApp.xcarchive \
-exportPath build \
-exportOptionsPlist ExportOptions.plist
# Move IPA file
cp build/MyAwesomeApp.ipa ../builds/ios/
cd ..
echo "✅ iOS build completed"
fi
# Bundle analysis
echo "📊 Running bundle analysis..."
npx react-native-bundle-visualizer --platform android --dev false --output analysis/android-bundle-analysis.html
npx react-native-bundle-visualizer --platform ios --dev false --output analysis/ios-bundle-analysis.html
# Generate release notes
echo "📝 Generating release notes..."
cat > "builds/release-notes-v$VERSION.md" << EOF
# Release Notes v$VERSION
## 📱 Application Information
- Version: $VERSION
- Build Date: $(date)
- React Native Version: $(npx react-native --version)
- Node.js Version: $NODE_VERSION
## 📦 Build Artifacts
- Android APK: builds/android/app-release.apk
- iOS IPA: builds/ios/MyAwesomeApp.ipa
## 🧪 Quality Assurance
- ✅ TypeScript Type Check Passed
- ✅ ESLint Check Passed
- ✅ Unit Tests Passed
- ✅ Bundle Analysis Completed
## 📊 Bundle Analysis
- Android Bundle Analysis: analysis/android-bundle-analysis.html
- iOS Bundle Analysis: analysis/ios-bundle-analysis.html
---
Generated automatically by release script.
EOF
echo "🎉 Release completed!"
echo "📄 Release Notes: builds/release-notes-v$VERSION.md"
echo "📱 Build files: Check builds/ directory"
# Success notification (optional)
if command -v osascript &> /dev/null; then
osascript -e "display notification \"React Native app release completed!\" with title \"🎉 Build Success\""
fi