एक्सपो और रिएक्ट नेटिव के साथ एंटरप्राइज मोबाइल ऐप्स का निर्माण
एक्सपो एक रैपिड प्रोटोटाइप टूल से एक वैध एंटरप्राइज़ प्लेटफ़ॉर्म में परिपक्व हो गया है। एक्सपो एसडीके 52 और नए आर्किटेक्चर (फैब्रिक रेंडरर + जेएसआई) के साथ, आप क्रॉस-प्लेटफ़ॉर्म मोबाइल ऐप बना सकते हैं जो मूल ऐप्स के प्रदर्शन और अनुभव से मेल खाते हैं - अपने रिएक्ट वेब एप्लिकेशन के साथ कोड साझा करते हुए और एक ही टाइपस्क्रिप्ट कोडबेस से आईओएस और एंड्रॉइड दोनों पर शिपिंग करते हुए।
यह मार्गदर्शिका उन पैटर्न को शामिल करती है जो एक हॉबी प्रोजेक्ट को प्रोडक्शन एंटरप्राइज ऐप से अलग करते हैं: ईएएस बिल्ड कॉन्फ़िगरेशन, पुश नोटिफिकेशन इंफ्रास्ट्रक्चर, ऑफ़लाइन-प्रथम डेटा सिंक, बायोमेट्रिक प्रमाणीकरण और ऐप स्टोर सबमिशन वर्कफ़्लो।
मुख्य बातें
- एक्सपो गो डेमो के लिए है; ईएएस बिल्ड उत्पादन के लिए है - पहले दिन से ईएएस से शुरुआत करें
- नया आर्किटेक्चर (फैब्रिक + जेएसआई) ऑप्ट-इन है लेकिन मापने योग्य प्रदर्शन लाभ प्रदान करता है
expo-notificationsको अग्रभूमि और पृष्ठभूमि प्रबंधन दोनों के लिए सावधानीपूर्वक सेटअप की आवश्यकता होती है- ऑफ़लाइन समर्थन के लिए सिंक लॉजिक के साथ एक स्थानीय डेटाबेस (WatermelonDB या SQLite) की आवश्यकता होती है
expo-local-authenticationके साथ बायोमेट्रिक प्रमाणीकरण कोड की कुछ पंक्तियाँ हैं- डीप लिंकिंग के लिए ऐप.जेसन और आपके प्रमाणीकरण प्रदाता के कॉलबैक यूआरएल दोनों में कॉन्फ़िगरेशन की आवश्यकता होती है
- टाइपस्क्रिप्ट पथ उपनाम
babel-plugin-module-resolverके माध्यम से एक्सपो में काम करते हैं- संवेदनशील डेटा को कभी भी
AsyncStorageमें संग्रहीत न करें - टोकन के लिएexpo-secure-storeका उपयोग करें
प्रोजेक्ट सेटअप
यदि आप किसी मूल मॉड्यूल आवश्यकता का अनुमान लगाते हैं तो प्रत्येक एक्सपो प्रोजेक्ट को बेयर वर्कफ़्लो (प्रबंधित नहीं) के साथ प्रारंभ करें। प्रबंधित वर्कफ़्लो सुविधाजनक है लेकिन आपको सीमित करता है - बाद में स्विच करने के लिए इजेक्टिंग की आवश्यकता होती है, जो विघटनकारी है।
# Create new Expo project
npx create-expo-app@latest MyApp --template
# Or with TypeScript template
npx create-expo-app@latest MyApp -t expo-template-blank-typescript
# Install core enterprise dependencies
npx expo install \
expo-router \
expo-secure-store \
expo-local-authentication \
expo-notifications \
expo-updates \
expo-constants \
@react-navigation/native \
@react-navigation/bottom-tabs \
@tanstack/react-query
expo-router फ़ाइल-आधारित रूटिंग सिस्टम आधुनिक दृष्टिकोण है - यह Next.js के ऐप राउटर पैटर्न को प्रतिबिंबित करता है, जिससे वेब और मोबाइल के बीच संदर्भ-स्विचिंग बहुत आसान हो जाती है।
ऐप.जेसन कॉन्फ़िगरेशन
एंटरप्राइज़ ऐप्स को प्रारंभ से ही सावधानीपूर्वक app.json कॉन्फ़िगरेशन की आवश्यकता होती है:
{
"expo": {
"name": "ECOSIRE",
"slug": "ecosire-mobile",
"version": "1.0.0",
"orientation": "portrait",
"icon": "./assets/icon.png",
"userInterfaceStyle": "automatic",
"splash": {
"image": "./assets/splash.png",
"resizeMode": "contain",
"backgroundColor": "#0a0a0a"
},
"ios": {
"supportsTablet": true,
"bundleIdentifier": "com.ecosire.mobile",
"buildNumber": "1",
"infoPlist": {
"NSFaceIDUsageDescription": "Use Face ID to securely sign in",
"NSCameraUsageDescription": "Take photos for your profile"
}
},
"android": {
"adaptiveIcon": {
"foregroundImage": "./assets/adaptive-icon.png",
"backgroundColor": "#0a0a0a"
},
"package": "com.ecosire.mobile",
"versionCode": 1,
"permissions": [
"USE_BIOMETRIC",
"USE_FINGERPRINT",
"RECEIVE_BOOT_COMPLETED",
"VIBRATE"
]
},
"plugins": [
[
"expo-notifications",
{
"icon": "./assets/notification-icon.png",
"color": "#f59e0b",
"sounds": ["./assets/notification.wav"]
}
],
"expo-router",
"expo-secure-store",
"expo-local-authentication"
],
"updates": {
"url": "https://u.expo.dev/YOUR-PROJECT-ID"
},
"runtimeVersion": {
"policy": "sdkVersion"
},
"extra": {
"eas": {
"projectId": "YOUR-PROJECT-ID"
}
}
}
}
ईएएस बिल्ड कॉन्फ़िगरेशन
ईएएस (एक्सपो एप्लीकेशन सर्विसेज) आपके ऐप को बनाने, सबमिट करने और अपडेट करने का काम संभालती है। इसे eas.json के साथ कॉन्फ़िगर करें:
{
"cli": {
"version": ">= 10.0.0",
"appVersionSource": "remote"
},
"build": {
"development": {
"developmentClient": true,
"distribution": "internal",
"ios": {
"simulator": true
},
"android": {
"buildType": "apk"
},
"env": {
"API_URL": "http://localhost:3001",
"NODE_ENV": "development"
}
},
"preview": {
"distribution": "internal",
"channel": "preview",
"env": {
"API_URL": "https://staging-api.ecosire.com",
"NODE_ENV": "staging"
}
},
"production": {
"channel": "production",
"autoIncrement": true,
"env": {
"API_URL": "https://api.ecosire.com",
"NODE_ENV": "production"
},
"ios": {
"credentialsSource": "remote"
},
"android": {
"credentialsSource": "remote"
}
}
},
"submit": {
"production": {
"ios": {
"appleId": "[email protected]",
"ascAppId": "YOUR-APP-STORE-CONNECT-APP-ID",
"appleTeamId": "YOUR-TEAM-ID"
},
"android": {
"serviceAccountKeyPath": "./google-service-account.json",
"track": "internal"
}
}
}
}
# Build for different environments
eas build --profile development --platform ios
eas build --profile preview --platform all
eas build --profile production --platform all
# Submit to stores
eas submit --profile production --platform ios
eas submit --profile production --platform android
सुरक्षित प्रमाणीकरण
एंटरप्राइज़ ऐप्स को कभी भी AsyncStorage में ऑथ टोकन संग्रहीत नहीं करना चाहिए - यह अनएन्क्रिप्टेड है और रूट किए गए डिवाइस पर अन्य ऐप्स के लिए पहुंच योग्य है। expo-secure-store का उपयोग करें जो किचेन (आईओएस) और कीस्टोर (एंड्रॉइड) का उपयोग करता है:
// lib/auth/token-storage.ts
import * as SecureStore from 'expo-secure-store';
const ACCESS_TOKEN_KEY = 'ecosire_access_token';
const REFRESH_TOKEN_KEY = 'ecosire_refresh_token';
export const tokenStorage = {
async saveTokens(accessToken: string, refreshToken: string) {
await Promise.all([
SecureStore.setItemAsync(ACCESS_TOKEN_KEY, accessToken, {
keychainService: 'com.ecosire.mobile.auth',
keychainAccessible: SecureStore.WHEN_UNLOCKED,
}),
SecureStore.setItemAsync(REFRESH_TOKEN_KEY, refreshToken, {
keychainService: 'com.ecosire.mobile.auth',
keychainAccessible: SecureStore.WHEN_UNLOCKED,
}),
]);
},
async getAccessToken(): Promise<string | null> {
return SecureStore.getItemAsync(ACCESS_TOKEN_KEY, {
keychainService: 'com.ecosire.mobile.auth',
});
},
async getRefreshToken(): Promise<string | null> {
return SecureStore.getItemAsync(REFRESH_TOKEN_KEY, {
keychainService: 'com.ecosire.mobile.auth',
});
},
async clearTokens() {
await Promise.all([
SecureStore.deleteItemAsync(ACCESS_TOKEN_KEY),
SecureStore.deleteItemAsync(REFRESH_TOKEN_KEY),
]);
},
};
बायोमेट्रिक प्रमाणीकरण
फेस आईडी/टच आईडी/फिंगरप्रिंट प्रमाणीकरण जोड़ा जा रहा है:
// lib/auth/biometric.ts
import * as LocalAuthentication from 'expo-local-authentication';
export async function isBiometricAvailable(): Promise<boolean> {
const compatible = await LocalAuthentication.hasHardwareAsync();
if (!compatible) return false;
const enrolled = await LocalAuthentication.isEnrolledAsync();
return enrolled;
}
export async function authenticateWithBiometrics(
reason = 'Confirm your identity'
): Promise<boolean> {
const result = await LocalAuthentication.authenticateAsync({
promptMessage: reason,
fallbackLabel: 'Use passcode',
disableDeviceFallback: false,
cancelLabel: 'Cancel',
});
return result.success;
}
// Usage in a component
async function handleSensitiveAction() {
const available = await isBiometricAvailable();
if (available) {
const authenticated = await authenticateWithBiometrics(
'Confirm this transaction'
);
if (!authenticated) return;
}
// Proceed with the action
await processSensitiveOperation();
}
पुश सूचनाएं
पुश नोटिफिकेशन के लिए बैकएंड इंफ्रास्ट्रक्चर और सावधानीपूर्वक अनुमति प्रबंधन की आवश्यकता होती है:
// lib/notifications/setup.ts
import * as Notifications from 'expo-notifications';
import * as Device from 'expo-device';
import Constants from 'expo-constants';
import { Platform } from 'react-native';
// Configure how notifications appear when app is in foreground
Notifications.setNotificationHandler({
handleNotification: async () => ({
shouldShowAlert: true,
shouldPlaySound: true,
shouldSetBadge: true,
}),
});
export async function registerForPushNotifications(): Promise<string | null> {
// Push notifications don't work on simulators
if (!Device.isDevice) {
console.warn('Push notifications require a physical device');
return null;
}
const { status: existingStatus } = await Notifications.getPermissionsAsync();
let finalStatus = existingStatus;
if (existingStatus !== 'granted') {
const { status } = await Notifications.requestPermissionsAsync();
finalStatus = status;
}
if (finalStatus !== 'granted') {
return null; // User denied permissions
}
// Android requires a notification channel
if (Platform.OS === 'android') {
await Notifications.setNotificationChannelAsync('default', {
name: 'Default',
importance: Notifications.AndroidImportance.MAX,
vibrationPattern: [0, 250, 250, 250],
lightColor: '#f59e0b',
});
}
const projectId = Constants.expoConfig?.extra?.eas?.projectId;
const token = await Notifications.getExpoPushTokenAsync({ projectId });
return token.data;
}
// hooks/use-push-notifications.ts
import { useEffect, useRef } from 'react';
import { AppState } from 'react-native';
import * as Notifications from 'expo-notifications';
import { registerForPushNotifications } from '@/lib/notifications/setup';
import { updatePushToken } from '@/lib/api/notifications';
export function usePushNotifications() {
const notificationListener = useRef<Notifications.Subscription>();
const responseListener = useRef<Notifications.Subscription>();
useEffect(() => {
// Register and save token to backend
registerForPushNotifications().then((token) => {
if (token) {
updatePushToken(token).catch(console.error);
}
});
// Handle notifications received while app is open
notificationListener.current =
Notifications.addNotificationReceivedListener((notification) => {
console.log('Notification received:', notification);
});
// Handle notification tap
responseListener.current =
Notifications.addNotificationResponseReceivedListener((response) => {
const data = response.notification.request.content.data;
// Navigate based on notification data
handleNotificationNavigation(data);
});
return () => {
notificationListener.current?.remove();
responseListener.current?.remove();
};
}, []);
}
वॉटरमेलनडीबी के साथ ऑफ़लाइन सहायता
एंटरप्राइज़ ऐप्स को ऑफ़लाइन काम करने की आवश्यकता है. वॉटरमेलनडीबी रिएक्ट नेटिव के लिए सबसे अधिक प्रदर्शन करने वाला स्थानीय डेटाबेस है:
npx expo install @nozbe/watermelondb @nozbe/with-observables
// db/schema.ts
import { appSchema, tableSchema } from '@nozbe/watermelondb';
export const schema = appSchema({
version: 1,
tables: [
tableSchema({
name: 'contacts',
columns: [
{ name: 'server_id', type: 'string', isOptional: true },
{ name: 'name', type: 'string' },
{ name: 'email', type: 'string', isOptional: true },
{ name: 'organization_id', type: 'string' },
{ name: 'synced_at', type: 'number', isOptional: true },
{ name: 'is_deleted', type: 'boolean' },
{ name: 'created_at', type: 'number' },
{ name: 'updated_at', type: 'number' },
],
}),
],
});
// lib/sync/contacts-sync.ts
import { synchronize } from '@nozbe/watermelondb/sync';
import { database } from '@/db';
export async function syncContacts(authToken: string) {
await synchronize({
database,
pullChanges: async ({ lastPulledAt }) => {
const response = await fetch(
`${API_URL}/sync/contacts?since=${lastPulledAt}`,
{
headers: { Authorization: `Bearer ${authToken}` },
}
);
const { changes, timestamp } = await response.json();
return { changes, timestamp };
},
pushChanges: async ({ changes }) => {
await fetch(`${API_URL}/sync/contacts`, {
method: 'POST',
headers: {
Authorization: `Bearer ${authToken}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ changes }),
});
},
migrationsEnabledAtVersion: 1,
});
}
एपीआई डेटा के लिए टैनस्टैक क्वेरी
टैनस्टैक क्वेरी रिएक्ट नेटिव में बिल्कुल रिएक्ट वेब ऐप्स की तरह काम करती है:
// lib/api/contacts.ts
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import { tokenStorage } from '@/lib/auth/token-storage';
async function apiRequest<T>(path: string, options?: RequestInit): Promise<T> {
const token = await tokenStorage.getAccessToken();
const response = await fetch(`${process.env.API_URL}${path}`, {
...options,
headers: {
'Content-Type': 'application/json',
...(token ? { Authorization: `Bearer ${token}` } : {}),
...options?.headers,
},
});
if (!response.ok) {
throw new Error(`API error: ${response.status}`);
}
return response.json();
}
export function useContacts() {
return useQuery({
queryKey: ['contacts'],
queryFn: () => apiRequest<Contact[]>('/contacts'),
staleTime: 5 * 60 * 1000, // 5 minutes
});
}
export function useCreateContact() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: (data: CreateContactDto) =>
apiRequest<Contact>('/contacts', {
method: 'POST',
body: JSON.stringify(data),
}),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['contacts'] });
},
});
}
एक्सपो-अपडेट के साथ ओवर-द-एयर अपडेट
ईएएस अपडेट ऐप स्टोर की समीक्षा के बिना जावास्क्रिप्ट बंडल अपडेट प्रदान करता है - बग फिक्स के लिए महत्वपूर्ण:
// hooks/use-app-updates.ts
import { useEffect } from 'react';
import * as Updates from 'expo-updates';
import { Alert } from 'react-native';
export function useAppUpdates() {
useEffect(() => {
async function checkForUpdates() {
if (__DEV__) return; // Skip in development
try {
const update = await Updates.checkForUpdateAsync();
if (update.isAvailable) {
await Updates.fetchUpdateAsync();
Alert.alert(
'Update Available',
'A new version of the app is ready. Restart to apply.',
[
{ text: 'Later', style: 'cancel' },
{
text: 'Restart Now',
onPress: () => Updates.reloadAsync(),
},
]
);
}
} catch (error) {
// Update check failed — app continues running current version
console.warn('Update check failed:', error);
}
}
checkForUpdates();
}, []);
}
अक्सर पूछे जाने वाले प्रश्न
मुझे बेयर रिएक्ट नेटिव के बजाय एक्सपो कब चुनना चाहिए?
अधिकांश एंटरप्राइज़ ऐप्स के लिए एक्सपो चुनें। ईएएस पारिस्थितिकी तंत्र 95% उद्यम आवश्यकताओं को कवर करता है - बायोमेट्रिक प्रमाणीकरण, पुश नोटिफिकेशन, डीप लिंकिंग, कैमरा, फ़ाइल सिस्टम, सुरक्षित भंडारण। केवल तभी रिएक्ट नेटिव पर जाएँ यदि आपको कस्टम नेटिव मॉड्यूल की आवश्यकता है जो एक्सपो में उपलब्ध नहीं है, या यदि आप लीगेसी नेटिव कोड के साथ एकीकृत कर रहे हैं। एक्सपो के साथ विकास का अनुभव काफी बेहतर है, और ईएएस बिल्ड देशी बिल्ड की जटिलता को संभालता है।
मैं नेक्स्ट.जेएस वेब ऐप और एक्सपो मोबाइल ऐप के बीच कोड कैसे साझा करूं?
व्यावसायिक तर्क, प्रकार और एपीआई क्लाइंट के लिए साझा पैकेज के साथ टर्बोरेपो मोनोरेपो का उपयोग करें। रिएक्ट नेटिव सभी ब्राउज़र/नोड.जेएस एपीआई का समर्थन नहीं करता है, इसलिए साझा पैकेजों में क्या होता है, इसके बारे में सावधान रहें। एक पैटर्न जो अच्छी तरह से काम करता है: शेयर प्रकार, सत्यापन स्कीमा (ज़ोड), और एपीआई क्लाइंट फ़ंक्शन, लेकिन यूआई घटकों को अलग रखें। Next.js 16 के ऐप राउटर और एक्सपो राउटर दोनों समान मानसिक मॉडल के साथ फ़ाइल-आधारित रूटिंग का उपयोग करते हैं, जिससे समानांतर कार्यान्वयन को बनाए रखना आसान हो जाता है।
मैं ऑथ कॉलबैक के लिए डीप लिंकिंग को कैसे प्रबंधित करूं?
अपनी URI योजना को scheme के अंतर्गत app.json में कॉन्फ़िगर करें (उदाहरण के लिए, "scheme": "ecosire")। अपने प्रमाणीकरण प्रदाता में ecosire://auth/callback को रीडायरेक्ट URI के रूप में पंजीकृत करें। अपने ऐप में आने वाले यूआरएल को संभालने के लिए expo-linking का उपयोग करें। वास्तविक यूआरएल की तरह काम करने वाले यूनिवर्सल लिंक (आईओएस) और ऐप लिंक (एंड्रॉइड) के लिए, आपको अतिरिक्त डोमेन सत्यापन की आवश्यकता है - apple-app-site-association और assetlinks.json फ़ाइलें जो आपके डोमेन से प्रदान की जाती हैं।
ईएएस में ऐप वर्जनिंग को संभालने का सबसे अच्छा तरीका क्या है?
"appVersionSource": "remote" को eas.json में सेट करें और उत्पादन बिल्ड के लिए "autoIncrement": true सक्षम करें। यह ईएएस को बिल्ड नंबरों को स्वचालित रूप से प्रबंधित करने देता है, जिससे मैन्युअल रूप से वेतन वृद्धि भूलने की सामान्य गलती को रोका जा सकता है। अपने मानव-पठनीय संस्करण (1.2.3) को app.json में रखें और EAS को बिल्ड नंबर (iOS buildNumber, Android versionCode) को संभालने दें।
मैं विकास में पुश सूचनाओं का परीक्षण कैसे करूं?
अपने डिवाइस के एक्सपो पुश टोकन के साथ https://expo.dev/notifications पर एक्सपो के पुश नोटिफिकेशन टेस्टिंग टूल का उपयोग करें। स्थानीय विकास के लिए, expo-notifications एक scheduleNotificationAsync फ़ंक्शन प्रदान करता है जो बिना बैकएंड के स्थानीय सूचनाएं ट्रिगर करता है। एंड-टू-एंड परीक्षण के लिए, eas build --profile development के साथ एक डेवलपमेंट क्लाइंट बनाएं और एक भौतिक डिवाइस का उपयोग करें।
अगले चरण
विश्वसनीय, सुरक्षित और बड़े पैमाने पर रखरखाव योग्य एंटरप्राइज़ मोबाइल ऐप बनाने के लिए सही नींव की आवश्यकता होती है - ईएएस बिल्ड कॉन्फ़िगरेशन, सुरक्षित टोकन स्टोरेज, पुश नोटिफिकेशन इंफ्रास्ट्रक्चर और ऑफ़लाइन सिंक सभी को पहले दिन से सावधानीपूर्वक सेटअप की आवश्यकता होती है।
ECOSIRE ने एक्सपो का उपयोग करके 11 स्क्रीन, अनंत स्क्रॉल, पुश नोटिफिकेशन और ईएएस परिनियोजन कॉन्फ़िगरेशन के साथ उत्पादन मोबाइल एप्लिकेशन का निर्माण किया है। यदि आपको मोबाइल विकास विशेषज्ञता की आवश्यकता है, हमारी विकास सेवाओं का पता लगाएं।
लेखक
ECOSIRE Research and Development Team
ECOSIRE में एंटरप्राइज़-ग्रेड डिजिटल उत्पाद बना रहे हैं। Odoo एकीकरण, ई-कॉमर्स ऑटोमेशन, और AI-संचालित व्यावसायिक समाधानों पर अंतर्दृष्टि साझा कर रहे हैं।
संबंधित लेख
Data Mesh Architecture: Decentralized Data for Enterprise
A comprehensive guide to data mesh architecture—principles, implementation patterns, organizational requirements, and how it enables scalable, domain-driven data ownership.
ECOSIRE vs Big 4 Consultancies: Enterprise Quality, Startup Speed
How ECOSIRE delivers enterprise-grade ERP and digital transformation outcomes without Big 4 pricing, overhead, or timeline bloat. A direct comparison.
Generative AI in Enterprise Applications: Beyond Chatbots
Discover how generative AI is transforming enterprise applications beyond chatbots—from code generation to synthetic data, document intelligence, and process automation.