React Native: Sviluppare un'App Mobile completa nel 2026

Guida completa allo sviluppo di applicazioni iOS e Android con React Native. Dal setup al deployment, tutti i fondamentali per iniziare.

Sviluppo di app mobile con React Native

React Native consente di creare applicazioni mobile native per iOS e Android a partire da un'unica codebase JavaScript. Mantenuto da Meta e adottato da app come Instagram, Facebook e Discord, il framework garantisce cicli di sviluppo rapidi senza sacrificare le prestazioni.

Perche React Native nel 2026?

La nuova architettura (Fabric e TurboModules) e ormai stabile e offre prestazioni quasi native mantenendo un'elevata produttivita di sviluppo. Oltre il 40 % delle 500 app piu utilizzate si affida a React Native.

Development Environment Setup

Prima di iniziare, occorre installare gli strumenti necessari. React Native propone due approcci: Expo (consigliato per chi parte da zero) e la React Native CLI (per un controllo piu granulare).

bash
# setup.sh
# Node.js installation (LTS version recommended)
# Check installed version
node --version  # >= 18.x required
npm --version   # >= 9.x required

# Install Expo CLI tool globally
npm install -g expo-cli

# Create a new project with Expo
npx create-expo-app@latest MyApp --template blank-typescript

# Navigate to the project
cd MyApp

# Start the development server
npx expo start

Expo semplifica notevolmente lo sviluppo, gestendo automaticamente la configurazione nativa. Per testare l'app basta l'app Expo Go sullo smartphone: si scansiona il QR code visualizzato nel terminale.

bash
# structure.sh
# Generated project structure
MyApp/
├── App.tsx              # Application entry point
├── app.json             # Expo configuration
├── package.json         # Dependencies
├── tsconfig.json        # TypeScript configuration
├── babel.config.js      # Babel configuration
└── assets/              # Images and resources
    ├── icon.png
    └── splash.png

Core React Native Components

React Native fornisce componenti nativi che sostituiscono gli elementi HTML. Ogni componente viene tradotto direttamente nel corrispondente componente nativo iOS o Android.

App.tsxtsx
import React from 'react';
import {
  View,
  Text,
  StyleSheet,
  SafeAreaView,
  StatusBar
} from 'react-native';

// Main application component
export default function App() {
  return (
    // SafeAreaView prevents overlap with status bar
    <SafeAreaView style={styles.container}>
      {/* StatusBar configures system bar appearance */}
      <StatusBar barStyle="dark-content" />

      {/* View is the equivalent of div */}
      <View style={styles.header}>
        {/* Text is required to display text */}
        <Text style={styles.title}>Welcome to MyApp</Text>
        <Text style={styles.subtitle}>
          A React Native application
        </Text>
      </View>
    </SafeAreaView>
  );
}

// StyleSheet.create optimizes styles for native
const styles = StyleSheet.create({
  container: {
    flex: 1,                    // Takes all available space
    backgroundColor: '#ffffff',
  },
  header: {
    padding: 20,
    alignItems: 'center',       // Centers horizontally
    justifyContent: 'center',   // Centers vertically
  },
  title: {
    fontSize: 24,
    fontWeight: 'bold',
    color: '#1a1a1a',
    marginBottom: 8,
  },
  subtitle: {
    fontSize: 16,
    color: '#666666',
  },
});
Flexbox di default

React Native utilizza Flexbox con flexDirection: 'column' come impostazione predefinita (a differenza del web che usa row). Questa differenza e fondamentale nella costruzione dei layout.

Handling User Interactions

Le interazioni touch vengono gestite tramite componenti specializzati. Ogni tipo di interazione dispone di un componente dedicato, ottimizzato per le prestazioni.

components/InteractiveButton.tsxtsx
import React, { useState } from 'react';
import {
  TouchableOpacity,
  TouchableHighlight,
  Pressable,
  Text,
  StyleSheet,
  View,
} from 'react-native';

// Button component with different interaction styles
export function InteractiveButton() {
  const [count, setCount] = useState(0);

  return (
    <View style={styles.container}>
      {/* TouchableOpacity reduces opacity on touch */}
      <TouchableOpacity
        style={styles.button}
        activeOpacity={0.7}
        onPress={() => setCount(c => c + 1)}
      >
        <Text style={styles.buttonText}>
          Counter: {count}
        </Text>
      </TouchableOpacity>

      {/* Pressable offers more control over states */}
      <Pressable
        style={({ pressed }) => [
          styles.button,
          styles.pressableButton,
          pressed && styles.buttonPressed,
        ]}
        onPress={() => console.log('Pressed!')}
        onLongPress={() => console.log('Long press!')}
      >
        {({ pressed }) => (
          <Text style={styles.buttonText}>
            {pressed ? 'Pressed!' : 'Press here'}
          </Text>
        )}
      </Pressable>
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    gap: 16,
    padding: 20,
  },
  button: {
    backgroundColor: '#007AFF',
    paddingVertical: 14,
    paddingHorizontal: 28,
    borderRadius: 10,
    alignItems: 'center',
  },
  pressableButton: {
    backgroundColor: '#34C759',
  },
  buttonPressed: {
    backgroundColor: '#2DA44E',
    transform: [{ scale: 0.98 }],
  },
  buttonText: {
    color: '#ffffff',
    fontSize: 16,
    fontWeight: '600',
  },
});

Screen Navigation with React Navigation

La navigazione e un elemento imprescindibile di qualsiasi app mobile. React Navigation rappresenta la soluzione standard e mette a disposizione diversi tipi di navigazione pensati per i pattern mobile.

bash
# install-navigation.sh
# Installing navigation dependencies
npx expo install @react-navigation/native
npx expo install @react-navigation/native-stack
npx expo install react-native-screens react-native-safe-area-context
App.tsxtsx
import React from 'react';
import { NavigationContainer } from '@react-navigation/native';
import { createNativeStackNavigator } from '@react-navigation/native-stack';

// Screen imports
import { HomeScreen } from './screens/HomeScreen';
import { DetailScreen } from './screens/DetailScreen';
import { ProfileScreen } from './screens/ProfileScreen';

// Type definition for TypeScript navigation
export type RootStackParamList = {
  Home: undefined;                    // No parameters
  Detail: { itemId: number; title: string };  // Required parameters
  Profile: { userId?: string };       // Optional parameter
};

// Creating typed navigator
const Stack = createNativeStackNavigator<RootStackParamList>();

export default function App() {
  return (
    // NavigationContainer manages navigation state
    <NavigationContainer>
      <Stack.Navigator
        initialRouteName="Home"
        screenOptions={{
          headerStyle: { backgroundColor: '#007AFF' },
          headerTintColor: '#ffffff',
          headerTitleStyle: { fontWeight: 'bold' },
        }}
      >
        <Stack.Screen
          name="Home"
          component={HomeScreen}
          options={{ title: 'Home' }}
        />
        <Stack.Screen
          name="Detail"
          component={DetailScreen}
          options={({ route }) => ({
            title: route.params.title
          })}
        />
        <Stack.Screen
          name="Profile"
          component={ProfileScreen}
        />
      </Stack.Navigator>
    </NavigationContainer>
  );
}
screens/HomeScreen.tsxtsx
import React from 'react';
import { View, Text, FlatList, TouchableOpacity, StyleSheet } from 'react-native';
import { NativeStackScreenProps } from '@react-navigation/native-stack';
import { RootStackParamList } from '../App';

// Typing navigation props
type Props = NativeStackScreenProps<RootStackParamList, 'Home'>;

// Sample data
const ITEMS = [
  { id: 1, title: 'First item' },
  { id: 2, title: 'Second item' },
  { id: 3, title: 'Third item' },
];

export function HomeScreen({ navigation }: Props) {
  return (
    <View style={styles.container}>
      {/* FlatList for performant lists */}
      <FlatList
        data={ITEMS}
        keyExtractor={(item) => item.id.toString()}
        renderItem={({ item }) => (
          <TouchableOpacity
            style={styles.item}
            onPress={() => {
              // Navigation with typed parameters
              navigation.navigate('Detail', {
                itemId: item.id,
                title: item.title,
              });
            }}
          >
            <Text style={styles.itemText}>{item.title}</Text>
          </TouchableOpacity>
        )}
        ItemSeparatorComponent={() => <View style={styles.separator} />}
      />
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#f5f5f5',
  },
  item: {
    backgroundColor: '#ffffff',
    padding: 20,
  },
  itemText: {
    fontSize: 16,
    color: '#1a1a1a',
  },
  separator: {
    height: 1,
    backgroundColor: '#e0e0e0',
  },
});

Pronto a superare i tuoi colloqui su React Native?

Pratica con i nostri simulatori interattivi, flashcards e test tecnici.

State Management with Context and Hooks

Per una gestione dello stato da semplice a intermedia, React Context abbinato agli hooks offre una soluzione efficace senza dipendenze esterne.

context/AuthContext.tsxtsx
import React, { createContext, useContext, useState, useCallback } from 'react';

// Types for authentication
interface User {
  id: string;
  email: string;
  name: string;
}

interface AuthContextType {
  user: User | null;
  isLoading: boolean;
  signIn: (email: string, password: string) => Promise<void>;
  signOut: () => void;
}

// Creating context with default value
const AuthContext = createContext<AuthContextType | undefined>(undefined);

// Provider that wraps the application
export function AuthProvider({ children }: { children: React.ReactNode }) {
  const [user, setUser] = useState<User | null>(null);
  const [isLoading, setIsLoading] = useState(false);

  // Sign in function
  const signIn = useCallback(async (email: string, password: string) => {
    setIsLoading(true);
    try {
      // Simulated API call
      const response = await fetch('https://api.example.com/login', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ email, password }),
      });
      const data = await response.json();
      setUser(data.user);
    } catch (error) {
      console.error('Login error:', error);
      throw error;
    } finally {
      setIsLoading(false);
    }
  }, []);

  // Sign out function
  const signOut = useCallback(() => {
    setUser(null);
  }, []);

  return (
    <AuthContext.Provider value={{ user, isLoading, signIn, signOut }}>
      {children}
    </AuthContext.Provider>
  );
}

// Custom hook to use the context
export function useAuth() {
  const context = useContext(AuthContext);
  if (context === undefined) {
    throw new Error('useAuth must be used within an AuthProvider');
  }
  return context;
}
screens/LoginScreen.tsxtsx
import React, { useState } from 'react';
import {
  View,
  Text,
  TextInput,
  TouchableOpacity,
  StyleSheet,
  ActivityIndicator,
  Alert,
} from 'react-native';
import { useAuth } from '../context/AuthContext';

export function LoginScreen() {
  const [email, setEmail] = useState('');
  const [password, setPassword] = useState('');
  const { signIn, isLoading } = useAuth();

  // Form submission handler
  const handleSubmit = async () => {
    if (!email || !password) {
      Alert.alert('Error', 'Please fill in all fields');
      return;
    }

    try {
      await signIn(email, password);
    } catch (error) {
      Alert.alert('Error', 'Invalid credentials');
    }
  };

  return (
    <View style={styles.container}>
      <Text style={styles.title}>Login</Text>

      <TextInput
        style={styles.input}
        placeholder="Email"
        value={email}
        onChangeText={setEmail}
        keyboardType="email-address"
        autoCapitalize="none"
        autoComplete="email"
      />

      <TextInput
        style={styles.input}
        placeholder="Password"
        value={password}
        onChangeText={setPassword}
        secureTextEntry
        autoComplete="password"
      />

      <TouchableOpacity
        style={[styles.button, isLoading && styles.buttonDisabled]}
        onPress={handleSubmit}
        disabled={isLoading}
      >
        {isLoading ? (
          <ActivityIndicator color="#ffffff" />
        ) : (
          <Text style={styles.buttonText}>Sign In</Text>
        )}
      </TouchableOpacity>
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    padding: 20,
    justifyContent: 'center',
    backgroundColor: '#ffffff',
  },
  title: {
    fontSize: 28,
    fontWeight: 'bold',
    marginBottom: 32,
    textAlign: 'center',
  },
  input: {
    borderWidth: 1,
    borderColor: '#e0e0e0',
    borderRadius: 10,
    padding: 16,
    marginBottom: 16,
    fontSize: 16,
    backgroundColor: '#f9f9f9',
  },
  button: {
    backgroundColor: '#007AFF',
    padding: 16,
    borderRadius: 10,
    alignItems: 'center',
    marginTop: 8,
  },
  buttonDisabled: {
    backgroundColor: '#99c9ff',
  },
  buttonText: {
    color: '#ffffff',
    fontSize: 18,
    fontWeight: '600',
  },
});
Stato globale vs. locale

Per applicazioni piu complesse con stato condiviso esteso, soluzioni come Zustand o Redux Toolkit possono risultare piu adatte. Context e ideale per lo stato che cambia raramente (tema, autenticazione).

API Calls and Data Management

La comunicazione con il backend e un aspetto centrale di qualsiasi app mobile. Il pattern seguente sfrutta un livello di astrazione per chiamate API robuste e manutenibili.

services/api.tstsx
// Base API configuration
const API_BASE_URL = 'https://api.example.com';

// Type for API errors
interface ApiError {
  message: string;
  code: string;
  status: number;
}

// Utility function for requests
async function request<T>(
  endpoint: string,
  options: RequestInit = {}
): Promise<T> {
  const url = `${API_BASE_URL}${endpoint}`;

  const config: RequestInit = {
    ...options,
    headers: {
      'Content-Type': 'application/json',
      ...options.headers,
    },
  };

  try {
    const response = await fetch(url, config);

    if (!response.ok) {
      const error: ApiError = await response.json();
      throw new Error(error.message || 'An error occurred');
    }

    return response.json();
  } catch (error) {
    // Network error handling
    if (error instanceof TypeError) {
      throw new Error('Network connection issue');
    }
    throw error;
  }
}

// Types for entities
interface Product {
  id: string;
  name: string;
  price: number;
  description: string;
  imageUrl: string;
}

// Products service
export const productApi = {
  // Get all products
  getAll: () => request<Product[]>('/products'),

  // Get product by ID
  getById: (id: string) => request<Product>(`/products/${id}`),

  // Create a product
  create: (data: Omit<Product, 'id'>) =>
    request<Product>('/products', {
      method: 'POST',
      body: JSON.stringify(data),
    }),

  // Update a product
  update: (id: string, data: Partial<Product>) =>
    request<Product>(`/products/${id}`, {
      method: 'PATCH',
      body: JSON.stringify(data),
    }),
};
hooks/useProducts.tstsx
import { useState, useEffect, useCallback } from 'react';
import { productApi } from '../services/api';

interface Product {
  id: string;
  name: string;
  price: number;
  description: string;
  imageUrl: string;
}

// Custom hook for product management
export function useProducts() {
  const [products, setProducts] = useState<Product[]>([]);
  const [isLoading, setIsLoading] = useState(true);
  const [error, setError] = useState<string | null>(null);

  // Initial products loading
  const fetchProducts = useCallback(async () => {
    setIsLoading(true);
    setError(null);

    try {
      const data = await productApi.getAll();
      setProducts(data);
    } catch (err) {
      setError(err instanceof Error ? err.message : 'Unknown error');
    } finally {
      setIsLoading(false);
    }
  }, []);

  // Refresh (pull-to-refresh)
  const refresh = useCallback(async () => {
    await fetchProducts();
  }, [fetchProducts]);

  // Load on mount
  useEffect(() => {
    fetchProducts();
  }, [fetchProducts]);

  return {
    products,
    isLoading,
    error,
    refresh,
  };
}
screens/ProductListScreen.tsxtsx
import React from 'react';
import {
  View,
  Text,
  FlatList,
  Image,
  StyleSheet,
  RefreshControl,
  ActivityIndicator,
} from 'react-native';
import { useProducts } from '../hooks/useProducts';

export function ProductListScreen() {
  const { products, isLoading, error, refresh } = useProducts();

  // Display during initial loading
  if (isLoading && products.length === 0) {
    return (
      <View style={styles.centered}>
        <ActivityIndicator size="large" color="#007AFF" />
        <Text style={styles.loadingText}>Loading...</Text>
      </View>
    );
  }

  // Display on error
  if (error) {
    return (
      <View style={styles.centered}>
        <Text style={styles.errorText}>{error}</Text>
      </View>
    );
  }

  return (
    <FlatList
      data={products}
      keyExtractor={(item) => item.id}
      contentContainerStyle={styles.list}
      // Pull-to-refresh
      refreshControl={
        <RefreshControl
          refreshing={isLoading}
          onRefresh={refresh}
          tintColor="#007AFF"
        />
      }
      renderItem={({ item }) => (
        <View style={styles.card}>
          <Image
            source={{ uri: item.imageUrl }}
            style={styles.image}
            resizeMode="cover"
          />
          <View style={styles.cardContent}>
            <Text style={styles.productName}>{item.name}</Text>
            <Text style={styles.productPrice}>
              ${item.price.toFixed(2)}
            </Text>
          </View>
        </View>
      )}
    />
  );
}

const styles = StyleSheet.create({
  centered: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    padding: 20,
  },
  loadingText: {
    marginTop: 12,
    fontSize: 16,
    color: '#666666',
  },
  errorText: {
    fontSize: 16,
    color: '#FF3B30',
    textAlign: 'center',
  },
  list: {
    padding: 16,
  },
  card: {
    backgroundColor: '#ffffff',
    borderRadius: 12,
    marginBottom: 16,
    shadowColor: '#000',
    shadowOffset: { width: 0, height: 2 },
    shadowOpacity: 0.1,
    shadowRadius: 4,
    elevation: 3,
  },
  image: {
    width: '100%',
    height: 200,
    borderTopLeftRadius: 12,
    borderTopRightRadius: 12,
  },
  cardContent: {
    padding: 16,
  },
  productName: {
    fontSize: 18,
    fontWeight: '600',
    color: '#1a1a1a',
    marginBottom: 4,
  },
  productPrice: {
    fontSize: 16,
    color: '#007AFF',
    fontWeight: '500',
  },
});

Local Data Storage

Lo storage locale permette di persistere i dati tra una sessione e l'altra. AsyncStorage e la soluzione standard per dati semplici, mentre SQLite si presta a dati strutturati piu complessi.

services/storage.tstsx
import AsyncStorage from '@react-native-async-storage/async-storage';

// Required installation: npx expo install @react-native-async-storage/async-storage

// Centralized storage keys
const STORAGE_KEYS = {
  USER_TOKEN: '@app/user_token',
  USER_PREFERENCES: '@app/user_preferences',
  ONBOARDING_COMPLETE: '@app/onboarding_complete',
} as const;

// Types for user preferences
interface UserPreferences {
  theme: 'light' | 'dark' | 'system';
  notifications: boolean;
  language: string;
}

// Typed storage service
export const storage = {
  // Authentication token
  async getToken(): Promise<string | null> {
    return AsyncStorage.getItem(STORAGE_KEYS.USER_TOKEN);
  },

  async setToken(token: string): Promise<void> {
    await AsyncStorage.setItem(STORAGE_KEYS.USER_TOKEN, token);
  },

  async removeToken(): Promise<void> {
    await AsyncStorage.removeItem(STORAGE_KEYS.USER_TOKEN);
  },

  // User preferences (JSON object)
  async getPreferences(): Promise<UserPreferences | null> {
    const data = await AsyncStorage.getItem(STORAGE_KEYS.USER_PREFERENCES);
    return data ? JSON.parse(data) : null;
  },

  async setPreferences(prefs: UserPreferences): Promise<void> {
    await AsyncStorage.setItem(
      STORAGE_KEYS.USER_PREFERENCES,
      JSON.stringify(prefs)
    );
  },

  // Onboarding
  async isOnboardingComplete(): Promise<boolean> {
    const value = await AsyncStorage.getItem(STORAGE_KEYS.ONBOARDING_COMPLETE);
    return value === 'true';
  },

  async setOnboardingComplete(): Promise<void> {
    await AsyncStorage.setItem(STORAGE_KEYS.ONBOARDING_COMPLETE, 'true');
  },

  // Complete cleanup
  async clearAll(): Promise<void> {
    const keys = Object.values(STORAGE_KEYS);
    await AsyncStorage.multiRemove(keys);
  },
};
Sicurezza dei dati sensibili

Per dati sensibili come i token di autenticazione, e consigliabile utilizzare expo-secure-store, che cifra i dati tramite iOS Keychain e Android Keystore.

Responsive Styles and Theming

Un'applicazione professionale deve adattarsi a diverse dimensioni di schermo e supportare la modalita scura.

theme/index.tstsx
import { Dimensions, PixelRatio, Platform } from 'react-native';

const { width: SCREEN_WIDTH, height: SCREEN_HEIGHT } = Dimensions.get('window');

// Reference dimensions (iPhone 14)
const guidelineBaseWidth = 390;
const guidelineBaseHeight = 844;

// Scaling functions
export const scale = (size: number) =>
  (SCREEN_WIDTH / guidelineBaseWidth) * size;

export const verticalScale = (size: number) =>
  (SCREEN_HEIGHT / guidelineBaseHeight) * size;

export const moderateScale = (size: number, factor = 0.5) =>
  size + (scale(size) - size) * factor;

// Light theme
export const lightTheme = {
  colors: {
    primary: '#007AFF',
    secondary: '#5856D6',
    success: '#34C759',
    warning: '#FF9500',
    error: '#FF3B30',
    background: '#FFFFFF',
    surface: '#F2F2F7',
    text: '#000000',
    textSecondary: '#8E8E93',
    border: '#E5E5EA',
  },
  spacing: {
    xs: scale(4),
    sm: scale(8),
    md: scale(16),
    lg: scale(24),
    xl: scale(32),
  },
  typography: {
    h1: {
      fontSize: moderateScale(32),
      fontWeight: 'bold' as const,
      lineHeight: moderateScale(40),
    },
    h2: {
      fontSize: moderateScale(24),
      fontWeight: 'bold' as const,
      lineHeight: moderateScale(32),
    },
    body: {
      fontSize: moderateScale(16),
      lineHeight: moderateScale(24),
    },
    caption: {
      fontSize: moderateScale(14),
      lineHeight: moderateScale(20),
    },
  },
  borderRadius: {
    sm: scale(4),
    md: scale(8),
    lg: scale(12),
    full: 9999,
  },
};

// Dark theme
export const darkTheme = {
  ...lightTheme,
  colors: {
    ...lightTheme.colors,
    background: '#000000',
    surface: '#1C1C1E',
    text: '#FFFFFF',
    textSecondary: '#8E8E93',
    border: '#38383A',
  },
};

export type Theme = typeof lightTheme;
context/ThemeContext.tsxtsx
import React, { createContext, useContext, useState, useEffect } from 'react';
import { useColorScheme } from 'react-native';
import { lightTheme, darkTheme, Theme } from '../theme';
import { storage } from '../services/storage';

interface ThemeContextType {
  theme: Theme;
  isDark: boolean;
  toggleTheme: () => void;
  setThemeMode: (mode: 'light' | 'dark' | 'system') => void;
}

const ThemeContext = createContext<ThemeContextType | undefined>(undefined);

export function ThemeProvider({ children }: { children: React.ReactNode }) {
  const systemColorScheme = useColorScheme();
  const [themeMode, setThemeMode] = useState<'light' | 'dark' | 'system'>('system');

  // Determine effective theme
  const isDark =
    themeMode === 'system'
      ? systemColorScheme === 'dark'
      : themeMode === 'dark';

  const theme = isDark ? darkTheme : lightTheme;

  // Load preferences on startup
  useEffect(() => {
    storage.getPreferences().then((prefs) => {
      if (prefs?.theme) {
        setThemeMode(prefs.theme);
      }
    });
  }, []);

  // Toggle between light and dark
  const toggleTheme = () => {
    const newMode = isDark ? 'light' : 'dark';
    setThemeMode(newMode);
    storage.getPreferences().then((prefs) => {
      storage.setPreferences({
        ...prefs,
        theme: newMode,
        notifications: prefs?.notifications ?? true,
        language: prefs?.language ?? 'en',
      });
    });
  };

  return (
    <ThemeContext.Provider value={{ theme, isDark, toggleTheme, setThemeMode }}>
      {children}
    </ThemeContext.Provider>
  );
}

export function useTheme() {
  const context = useContext(ThemeContext);
  if (!context) {
    throw new Error('useTheme must be used within a ThemeProvider');
  }
  return context;
}

Preparing for Publication

Prima di pubblicare un'app, sono necessari diversi passaggi di configurazione e ottimizzazione.

app.jsonjson
{
  "expo": {
    "name": "MyApp",
    "slug": "my-app",
    "version": "1.0.0",
    "orientation": "portrait",
    "icon": "./assets/icon.png",
    "userInterfaceStyle": "automatic",
    "splash": {
      "image": "./assets/splash.png",
      "resizeMode": "contain",
      "backgroundColor": "#007AFF"
    },
    "ios": {
      "supportsTablet": true,
      "bundleIdentifier": "com.example.myapp",
      "buildNumber": "1"
    },
    "android": {
      "adaptiveIcon": {
        "foregroundImage": "./assets/adaptive-icon.png",
        "backgroundColor": "#007AFF"
      },
      "package": "com.example.myapp",
      "versionCode": 1
    },
    "plugins": [
      "expo-router"
    ]
  }
}
bash
# build.sh
# EAS Build configuration (Expo Application Services)
npm install -g eas-cli

# Log in to Expo account
eas login

# Configure the project
eas build:configure

# Build for iOS (simulator)
eas build --platform ios --profile development

# Build for Android (test APK)
eas build --platform android --profile preview

# Production build
eas build --platform all --profile production

# Store submission
eas submit --platform ios
eas submit --platform android
Certificati e chiavi

La pubblicazione su iOS richiede un account Apple Developer (99 $/anno) e quella su Android un account Google Play Console (25 $ una tantum). EAS gestisce automaticamente i certificati di firma.

Conclusion

React Native offre un approccio efficiente allo sviluppo di app mobile multipiattaforma. I fondamentali trattati in questa guida consentono di realizzare applicazioni complete e professionali.

Checklist per un'app React Native di successo

  • Configurare l'ambiente di sviluppo con Expo per partire rapidamente
  • Padroneggiare i componenti principali: View, Text, TouchableOpacity, FlatList
  • Implementare la navigazione con React Navigation e la tipizzazione TypeScript
  • Gestire lo stato con la Context API e custom hook
  • Strutturare le chiamate API con un livello di servizi dedicato
  • Persistere i dati localmente con AsyncStorage
  • Creare un sistema di theming responsivo con supporto al dark mode

Inizia a praticare!

Metti alla prova le tue conoscenze con i nostri simulatori di colloquio e test tecnici.

La documentazione ufficiale di React Native e di Expo resta il punto di riferimento per approfondire ogni argomento. L'ecosistema evolve rapidamente e librerie come React Query o Zustand possono semplificare ulteriormente la gestione dei dati in applicazioni piu complesse.

Sfida del giorno

Sapresti trovare il bug in React Native?

Uno snippet reale, un bug nascosto, un tentativo al giorno. Senza account per provare.

Anthony Fillion-Maillet

Scritto da

Anthony Fillion-Maillet

Fondatore di SharpSkill

Sviluppatore fullstack da oltre 10 anni. Guida SharpSkill e risponde di tutto ciò che vi viene pubblicato.

Aggiornato il 6 aprile 2026

Tag

#react native
#mobile development
#javascript
#ios
#android

Condividi

Articoli correlati