Práctica: App de fotos con API y almacenamiento

Lectura
30 min~5 min lectura

Concepto clave

En el desarrollo de aplicaciones móviles modernas, la integración de APIs externas con funcionalidades nativas del dispositivo es fundamental para crear experiencias ricas y útiles. Imagina que estás construyendo una app de fotos: necesitas acceder a la cámara del teléfono (funcionalidad nativa) y luego subir esas imágenes a un servidor (API externa). Esta lección te enseñará cómo combinar ambas cosas usando React Native con Expo.

Piensa en esto como un restaurante: la cocina (tu app) tiene ingredientes locales (fotos del dispositivo) pero también necesita pedir suministros especiales (datos de un servidor). Expo actúa como el chef que sabe cómo manejar ambos flujos de trabajo de manera eficiente, usando herramientas como expo-image-picker para la cámara y fetch o axios para las APIs.

Cómo funciona en la práctica

Vamos a construir una app que permite al usuario tomar una foto con la cámara de su dispositivo, mostrar una vista previa, y luego subirla a un servidor usando una API REST. El proceso sigue estos pasos:

  1. Solicitar permisos para acceder a la cámara y la galería del dispositivo.
  2. Usar expo-image-picker para abrir la interfaz de cámara y capturar una imagen.
  3. Mostrar la imagen seleccionada en un componente Image de React Native.
  4. Crear un formulario con datos adicionales (como un título) usando useState.
  5. Enviar la imagen y los datos a un endpoint de API mediante una petición POST multipart/form-data.
  6. Manejar la respuesta del servidor y actualizar el estado de la app.

Este flujo es común en apps sociales, herramientas de inventario, o cualquier aplicación que requiera contenido generado por el usuario.

Código en acción

Aquí tienes un ejemplo básico de cómo implementar la captura de una foto:

import React, { useState } from 'react';
import { Button, Image, View, StyleSheet } from 'react-native';
import * as ImagePicker from 'expo-image-picker';

export default function PhotoCapture() {
  const [image, setImage] = useState(null);

  const pickImage = async () => {
    let result = await ImagePicker.launchCameraAsync({
      mediaTypes: ImagePicker.MediaTypeOptions.Images,
      allowsEditing: true,
      aspect: [4, 3],
      quality: 1,
    });

    if (!result.canceled) {
      setImage(result.assets[0].uri);
    }
  };

  return (
    
      
      {image && }
    
  );
}

const styles = StyleSheet.create({
  container: { flex: 1, alignItems: 'center', justifyContent: 'center' },
  image: { width: 200, height: 200, marginTop: 20 },
});

Ahora, vamos a mejorar este código añadiendo la funcionalidad de subir la foto a una API. Observa cómo refactorizamos para manejar el envío:

import React, { useState } from 'react';
import { Button, Image, View, TextInput, Alert, StyleSheet } from 'react-native';
import * as ImagePicker from 'expo-image-picker';

export default function PhotoUploader() {
  const [image, setImage] = useState(null);
  const [title, setTitle] = useState('');
  const [uploading, setUploading] = useState(false);

  const pickImage = async () => {
    let result = await ImagePicker.launchCameraAsync({
      mediaTypes: ImagePicker.MediaTypeOptions.Images,
      allowsEditing: true,
      aspect: [4, 3],
      quality: 0.8,
    });

    if (!result.canceled) {
      setImage(result.assets[0]);
    }
  };

  const uploadImage = async () => {
    if (!image || !title.trim()) {
      Alert.alert('Error', 'Por favor, toma una foto y añade un título');
      return;
    }

    setUploading(true);
    const formData = new FormData();
    formData.append('title', title);
    formData.append('photo', {
      uri: image.uri,
      type: 'image/jpeg',
      name: 'photo.jpg',
    });

    try {
      const response = await fetch('https://api.ejemplo.com/upload', {
        method: 'POST',
        body: formData,
        headers: {
          'Content-Type': 'multipart/form-data',
        },
      });

      const data = await response.json();
      if (response.ok) {
        Alert.alert('Éxito', 'Foto subida correctamente');
        setImage(null);
        setTitle('');
      } else {
        Alert.alert('Error', data.message || 'Falló la subida');
      }
    } catch (error) {
      Alert.alert('Error', 'No se pudo conectar al servidor');
    } finally {
      setUploading(false);
    }
  };

  return (
    
      
      {image && }
      
      
    
  );
}

const styles = StyleSheet.create({
  container: { flex: 1, padding: 20, alignItems: 'center' },
  image: { width: 200, height: 200, marginVertical: 20 },
  input: { borderWidth: 1, borderColor: '#ccc', padding: 10, width: '100%', marginBottom: 20 },
});

Errores comunes

  • Olvidar solicitar permisos: En iOS y Android, necesitas configurar permisos en app.json y solicitarlos en tiempo de ejecución. Usa ImagePicker.requestCameraPermissionsAsync() antes de abrir la cámara.
  • Manejo incorrecto de FormData: Al subir archivos, asegúrate de que el objeto en formData.append() tenga las propiedades uri, type, y name. Sin esto, el servidor puede rechazar la petición.
  • No validar entradas del usuario: Siempre verifica que la imagen y campos como el título estén presentes antes de enviar, para evitar errores 400 del servidor.
  • Ignorar estados de carga: Deshabilita botones durante la subida con un estado uploading para prevenir múltiples envíos y mejorar la experiencia de usuario.
  • Manejo pobre de errores de red: Envuelve las peticiones fetch en try-catch para capturar fallos de conexión y mostrar mensajes claros al usuario.

Checklist de dominio

  1. Sé cómo solicitar y verificar permisos para cámara y galería en Expo.
  2. Puedo capturar una foto usando expo-image-picker y mostrarla en pantalla.
  3. Entiendo cómo crear un FormData para subir archivos a una API REST.
  4. Sé manejar estados de carga y error durante las peticiones de red.
  5. Puedo integrar entradas de usuario (como TextInput) con la funcionalidad de subida.
  6. Comprendo la importancia de validar datos antes de enviarlos al servidor.
  7. Soy capaz de probar la app en un dispositivo real o emulador para verificar el flujo completo.

Construye una app de galería de fotos con subida a Cloudinary

En este ejercicio, crearás una app que permite a los usuarios tomar fotos, añadir descripciones, y subirlas a Cloudinary (un servicio de almacenamiento en la nube). Sigue estos pasos:

  1. Configura un proyecto nuevo de Expo con npx create-expo-app PhotoGallery.
  2. Instala las dependencias necesarias: expo-image-picker y axios.
  3. Crea una cuenta gratuita en Cloudinary y obtén tu cloud name y upload preset.
  4. Implementa un componente que solicite permisos de cámara, capture una foto, y muestre una vista previa.
  5. Añade un campo TextInput para que el usuario ingrese una descripción de la foto.
  6. Configura una función que suba la foto a Cloudinary usando axios, enviando la imagen como FormData.
  7. Muestra un indicador de carga durante la subida y un mensaje de éxito o error al finalizar.
  8. Prueba la app en tu dispositivo o emulador, verificando que la foto se suba correctamente y aparezca en tu dashboard de Cloudinary.
Pistas
  • Recuerda configurar los permisos en app.json para iOS y Android antes de probar en dispositivo.
  • Cloudinary requiere enviar el archivo en un campo llamado 'file' en el FormData, consulta su documentación para detalles.
  • Usa el estado uploading para deshabilitar botones y mostrar un ActivityIndicator durante la subida.

Evalua tu comprension

Completa el quiz interactivo de arriba para ganar XP.