29. Creación de Json (Simulando Api)

 

MusicApp

Introducción

¡Hola de nuevo! En este vídeo vamos a crear la estructura de datos para nuestra aplicación de música. Vamos a simular una API local usando un archivo JSON que contenga toda la información que necesitaremos: imágenes, títulos, y configuraciones para cada sección de nuestra app.

Paso 1: Estructura del Proyecto

Primero, vamos a organizar nuestro proyecto. Necesitamos crear una carpeta src y dentro de ella organizar nuestras imágenes y datos:

text

MusicApp/

├── src/

│   ├── images/

│   │   ├── banner.png

│   │   ├── right-arrow.png

│   │   ├── moodweekend/

│   │   │   ├── 1.png

│   │   │   ├── 2.png

│   │   │   ├── 3.png

│   │   │   ├── 4.png

│   │   │   └── 5.png

│   │   ├── topalbums/

│   │   │   ├── 1.png

│   │   │   ├── 2.png

│   │   │   ├── 3.png

│   │   │   └── 4.png

│   │   └── browseall/

│   │       ├── 1.png

│   │       ├── 2.png

│   │       ├── 3.png

│   │       ├── 4.png

│   │       ├── 5.png

│   │       ├── 6.png

│   │       ├── 7.png

│   │       ├── 8.png

│   │       ├── 9.png

│   │       └── 10.png

│   └── data.js

├── App.js

└── ...

Paso 2: Crear el Archivo data.js

Dentro de la carpeta src, creamos un nuevo archivo llamado data.js:

src/data.js

javascript

// Archivo de datos que simula una API

// Contiene toda la información estática de la aplicación


const data = {

  // Sección Banner

  banner: {

    img: require('./images/banner.png'),

    title: "Descubre nueva música",

    subtitle: "Explora miles de canciones y álbumes"

  },

  

  // Sección Mood Weekend

  moodWeekend: {

    title: "Mood Weekend",

    rightIcon: require('./images/right-arrow.png'),

    images: [

      {

        id: 1,

        img: require('./images/moodweekend/1.png'),

        title: "Relax",

        color: "#667eea"

      },

      {

        id: 2,

        img: require('./images/moodweekend/2.png'),

        title: "Energía",

        color: "#f093fb"

      },

      {

        id: 3,

        img: require('./images/moodweekend/3.png'),

        title: "Nostalgia",

        color: "#f5576c"

      },

      {

        id: 4,

        img: require('./images/moodweekend/4.png'),

        title: "Felicidad",

        color: "#4facfe"

      },

      {

        id: 5,

        img: require('./images/moodweekend/5.png'),

        title: "Concentración",

        color: "#43e97b"

      }

    ]

  },

  

  // Sección Top Albums

  topAlbums: {

    title: "Top Albums",

    rightIcon: require('./images/right-arrow.png'),

    images: [

      {

        id: 1,

        img: require('./images/topalbums/1.png'),

        title: "Midnight Dreams",

        artist: "The Dreamers",

        plays: "2.5M"

      },

      {

        id: 2,

        img: require('./images/topalbums/2.png'),

        title: "Summer Vibes",

        artist: "Ocean View",

        plays: "1.8M"

      },

      {

        id: 3,

        img: require('./images/topalbums/3.png'),

        title: "Urban Legends",

        artist: "City Lights",

        plays: "3.2M"

      },

      {

        id: 4,

        img: require('./images/topalbums/4.png'),

        title: "Echoes of Time",

        artist: "Nova Sequence",

        plays: "1.2M"

      }

    ]

  },

  

  // Sección Browse All

  browseAll: {

    title: "Browse All",

    images: [

      {

        id: 1,

        img: require('./images/browseall/1.png'),

        title: "Pop",

        color: "#8A2387"

      },

      {

        id: 2,

        img: require('./images/browseall/2.png'),

        title: "Rock",

        color: "#F27121"

      },

      {

        id: 3,

        img: require('./images/browseall/3.png'),

        title: "Hip Hop",

        color: "#E94057"

      },

      {

        id: 4,

        img: require('./images/browseall/4.png'),

        title: "Jazz",

        color: "#4A00E0"

      },

      {

        id: 5,

        img: require('./images/browseall/5.png'),

        title: "Electrónica",

        color: "#24C6DC"

      },

      {

        id: 6,

        img: require('./images/browseall/6.png'),

        title: "Indie",

        color: "#514A9D"

      },

      {

        id: 7,

        img: require('./images/browseall/7.png'),

        title: "Reggaetón",

        color: "#FF8008"

      },

      {

        id: 8,

        img: require('./images/browseall/8.png'),

        title: "Clásica",

        color: "#1D976C"

      },

      {

        id: 9,

        img: require('./images/browseall/9.png'),

        title: "R&B",

        color: "#DA4453"

      },

      {

        id: 10,

        img: require('./images/browseall/10.png'),

        title: "Country",

        color: "#D66D75"

      }

    ]

  }

};


export default data;

Paso 3: Explicación del Código

1. Estructura del objeto data:

javascript

const data = {

  // Propiedades principales

};

Creamos un objeto constante que contendrá todos nuestros datos estructurados.

2. Sección Banner:

javascript

banner: {

  img: require('./images/banner.png'),

  title: "Descubre nueva música",

  subtitle: "Explora miles de canciones y álbumes"

}

  • require(): Método de React Native para cargar imágenes locales

  • Contiene la imagen, título y subtítulo del banner principal

3. Sección Mood Weekend:

javascript

moodWeekend: {

  title: "Mood Weekend",

  rightIcon: require('./images/right-arrow.png'),

  images: [...]

}

  • rightIcon: Icono de flecha derecha para navegación

  • images: Array de objetos, cada uno con:

    • id: Identificador único

    • img: Imagen del mood

    • title: Nombre del mood

    • color: Color de fondo

4. Sección Top Albums:

Similar a Mood Weekend pero con información específica de álbumes:

javascript

{

  id: 1,

  img: require('./images/topalbums/1.png'),

  title: "Midnight Dreams",

  artist: "The Dreamers",

  plays: "2.5M"

}

5. Sección Browse All:

Contiene 10 categorías musicales, cada una con imagen, título y color.

Paso 4: Crear Versión Alternativa con Más Detalles

src/data-enhanced.js (opcional)

javascript

// Versión mejorada con más datos y categorías


const musicData = {

  appInfo: {

    name: "MusicApp",

    version: "1.0.0",

    developer: "Tu Nombre"

  },

  

  banner: {

    id: "banner_001",

    type: "promotional",

    img: require('./images/banner.png'),

    title: "🎵 Descubre Nueva Música",

    subtitle: "Explora miles de canciones y álbumes",

    ctaText: "Comenzar Ahora",

    backgroundColor: "linear-gradient(135deg, #667eea 0%, #764ba2 100%)",

    textColor: "#FFFFFF"

  },

  

  moodWeekend: {

    id: "section_mood",

    type: "horizontal_scroll",

    title: "Mood Weekend",

    description: "Playlists para cada estado de ánimo",

    rightIcon: {

      img: require('./images/right-arrow.png'),

      action: "view_all_moods"

    },

    items: [

      {

        id: "mood_001",

        type: "mood",

        img: require('./images/moodweekend/1.png'),

        title: "Relax",

        description: "Música para relajarte",

        color: "#667eea",

        songCount: 125,

        duration: "8h 45m"

      },

      // ... más moods

    ]

  },

  

  topAlbums: {

    id: "section_top_albums",

    type: "horizontal_scroll",

    title: "🔥 Top Albums",

    description: "Los álbumes más escuchados esta semana",

    filters: ["Hoy", "Esta semana", "Este mes"],

    rightIcon: {

      img: require('./images/right-arrow.png'),

      action: "view_all_albums"

    },

    items: [

      {

        id: "album_001",

        type: "album",

        rank: 1,

        img: require('./images/topalbums/1.png'),

        title: "Midnight Dreams",

        artist: {

          name: "The Dreamers",

          id: "artist_001"

        },

        year: 2023,

        genre: ["Indie", "Alternative"],

        plays: "2.5M",

        duration: "45:20",

        songs: 12,

        isExplicit: false

      },

      // ... más álbumes

    ]

  },

  

  browseAll: {

    id: "section_browse",

    type: "grid",

    title: "🌍 Browse All",

    description: "Explora todas las categorías musicales",

    layout: "2_columns", // o "3_columns" en tablets

    items: [

      {

        id: "category_001",

        type: "category",

        img: require('./images/browseall/1.png'),

        title: "Pop",

        description: "Música popular contemporánea",

        color: "#8A2387",

        gradient: ["#8A2387", "#F27121"],

        songCount: "50K+",

        featuredArtists: ["Taylor Swift", "Dua Lipa", "The Weeknd"]

      },

      // ... más categorías

    ]

  },

  

  recentlyPlayed: {

    id: "section_recent",

    type: "horizontal_scroll",

    title: "▶️ Recently Played",

    items: [

      // Datos de reproducción reciente

    ]

  },

  

  userProfile: {

    username: "Usuario",

    favoriteGenres: ["Pop", "Rock", "Hip Hop"],

    playlists: 15,

    followers: 245,

    following: 189

  }

};


// Función para obtener datos específicos

export const getSectionData = (sectionId) => {

  switch(sectionId) {

    case 'banner':

      return musicData.banner;

    case 'moodWeekend':

      return musicData.moodWeekend;

    case 'topAlbums':

      return musicData.topAlbums;

    case 'browseAll':

      return musicData.browseAll;

    default:

      return null;

  }

};


// Función para buscar items

export const searchItems = (query) => {

  const allItems = [

    ...musicData.moodWeekend.items,

    ...musicData.topAlbums.items,

    ...musicData.browseAll.items

  ];

  

  return allItems.filter(item => 

    item.title.toLowerCase().includes(query.toLowerCase()) ||

    (item.artist && item.artist.name.toLowerCase().includes(query.toLowerCase()))

  );

};


export default musicData;

Paso 5: Actualizar App.js para Usar los Datos

App.js actualizado:

javascript

import React from 'react';

import { 

  View, 

  Text, 

  StyleSheet, 

  SafeAreaView, 

  ScrollView,

  Image,

  TouchableOpacity 

} from 'react-native';


// Importamos nuestros datos

import data from './src/data';


const App = () => {

  return (

    <SafeAreaView style={styles.container}>

      <ScrollView style={styles.scrollView}>

        

        {/* Banner */}

        <View style={styles.banner}>

          <Image 

            source={data.banner.img} 

            style={styles.bannerImage}

            resizeMode="cover"

          />

          <View style={styles.bannerOverlay}>

            <Text style={styles.bannerTitle}>{data.banner.title}</Text>

            <Text style={styles.bannerSubtitle}>{data.banner.subtitle}</Text>

          </View>

        </View>

        

        {/* Mood Weekend Section */}

        <View style={styles.section}>

          <View style={styles.sectionHeader}>

            <Text style={styles.sectionTitle}>{data.moodWeekend.title}</Text>

            <TouchableOpacity>

              <Image 

                source={data.moodWeekend.rightIcon} 

                style={styles.rightIcon}

              />

            </TouchableOpacity>

          </View>

          

          <ScrollView 

            horizontal 

            showsHorizontalScrollIndicator={false}

            style={styles.horizontalScroll}

          >

            {data.moodWeekend.images.map((item) => (

              <TouchableOpacity key={item.id} style={styles.moodCard}>

                <Image 

                  source={item.img} 

                  style={styles.moodImage}

                  resizeMode="cover"

                />

                <Text style={styles.moodTitle}>{item.title}</Text>

              </TouchableOpacity>

            ))}

          </ScrollView>

        </View>

        

        {/* Top Albums Section */}

        <View style={styles.section}>

          <View style={styles.sectionHeader}>

            <Text style={styles.sectionTitle}>{data.topAlbums.title}</Text>

            <TouchableOpacity>

              <Image 

                source={data.topAlbums.rightIcon} 

                style={styles.rightIcon}

              />

            </TouchableOpacity>

          </View>

          

          <ScrollView 

            horizontal 

            showsHorizontalScrollIndicator={false}

            style={styles.horizontalScroll}

          >

            {data.topAlbums.images.map((album) => (

              <TouchableOpacity key={album.id} style={styles.albumCard}>

                <Image 

                  source={album.img} 

                  style={styles.albumImage}

                  resizeMode="cover"

                />

                <View style={styles.albumInfo}>

                  <Text style={styles.albumTitle}>{album.title}</Text>

                  <Text style={styles.albumArtist}>{album.artist}</Text>

                  <Text style={styles.albumPlays}>{album.plays} plays</Text>

                </View>

              </TouchableOpacity>

            ))}

          </ScrollView>

        </View>

        

        {/* Browse All Section */}

        <View style={styles.section}>

          <Text style={styles.sectionTitle}>{data.browseAll.title}</Text>

          

          <View style={styles.browseGrid}>

            {data.browseAll.images.map((category) => (

              <TouchableOpacity 

                key={category.id} 

                style={[styles.categoryCard, { backgroundColor: category.color }]}

              >

                <Image 

                  source={category.img} 

                  style={styles.categoryImage}

                  resizeMode="cover"

                />

                <Text style={styles.categoryTitle}>{category.title}</Text>

              </TouchableOpacity>

            ))}

          </View>

        </View>

        

      </ScrollView>

    </SafeAreaView>

  );

};


const styles = StyleSheet.create({

  container: {

    flex: 1,

    backgroundColor: '#121212',

  },

  scrollView: {

    flex: 1,

  },

  banner: {

    height: 200,

    borderRadius: 15,

    margin: 15,

    overflow: 'hidden',

    position: 'relative',

  },

  bannerImage: {

    width: '100%',

    height: '100%',

  },

  bannerOverlay: {

    position: 'absolute',

    bottom: 0,

    left: 0,

    right: 0,

    backgroundColor: 'rgba(0,0,0,0.5)',

    padding: 20,

  },

  bannerTitle: {

    fontSize: 24,

    fontWeight: 'bold',

    color: '#FFFFFF',

  },

  bannerSubtitle: {

    fontSize: 16,

    color: '#CCCCCC',

    marginTop: 5,

  },

  section: {

    paddingHorizontal: 15,

    marginBottom: 30,

  },

  sectionHeader: {

    flexDirection: 'row',

    justifyContent: 'space-between',

    alignItems: 'center',

    marginBottom: 15,

  },

  sectionTitle: {

    fontSize: 22,

    fontWeight: 'bold',

    color: '#FFFFFF',

  },

  rightIcon: {

    width: 24,

    height: 24,

    tintColor: '#1DB954',

  },

  horizontalScroll: {

    marginHorizontal: -15,

  },

  moodCard: {

    width: 150,

    marginRight: 15,

  },

  moodImage: {

    width: 150,

    height: 150,

    borderRadius: 10,

    marginBottom: 10,

  },

  moodTitle: {

    fontSize: 16,

    fontWeight: '600',

    color: '#FFFFFF',

    textAlign: 'center',

  },

  albumCard: {

    width: 180,

    marginRight: 15,

    backgroundColor: '#1E1E1E',

    borderRadius: 10,

    overflow: 'hidden',

  },

  albumImage: {

    width: '100%',

    height: 180,

  },

  albumInfo: {

    padding: 10,

  },

  albumTitle: {

    fontSize: 16,

    fontWeight: 'bold',

    color: '#FFFFFF',

    marginBottom: 5,

  },

  albumArtist: {

    fontSize: 14,

    color: '#B3B3B3',

    marginBottom: 5,

  },

  albumPlays: {

    fontSize: 12,

    color: '#1DB954',

  },

  browseGrid: {

    flexDirection: 'row',

    flexWrap: 'wrap',

    justifyContent: 'space-between',

    marginHorizontal: -5,

  },

  categoryCard: {

    width: '48%',

    height: 100,

    borderRadius: 10,

    marginBottom: 10,

    overflow: 'hidden',

    position: 'relative',

  },

  categoryImage: {

    width: '100%',

    height: '100%',

    opacity: 0.7,

  },

  categoryTitle: {

    position: 'absolute',

    bottom: 10,

    left: 10,

    fontSize: 18,

    fontWeight: 'bold',

    color: '#FFFFFF',

  },

});


export default App;

Paso 6: Ventajas de Esta Estructura

✅ Ventajas de usar data.js como API simulada:

  1. Organización centralizada: Todos los datos en un solo lugar

  2. Fácil mantenimiento: Cambios en un solo archivo

  3. Simulación realista: Estructura similar a una API real

  4. Rápido desarrollo: Sin dependencia de servidores externos

  5. Fácil migración: Cambiar a API real será sencillo

🔄 Cómo migrar a API real en el futuro:

javascript

// Futura implementación con API real

const fetchData = async () => {

  try {

    const response = await fetch('https://api.musicapp.com/data');

    const realData = await response.json();

    return realData;

  } catch (error) {

    // Fallback a datos locales

    return data;

  }

};

Paso 7: Consejos Adicionales

1. Tipos de datos para imágenes:

  • PNG: Para logos y gráficos con transparencia

  • JPG: Para fotografías y fondos

  • SVG: Para iconos escalables (necesita librería adicional)

2. Optimización de imágenes:

  • Reducir tamaño de imágenes antes de incluirlas

  • Usar dimensiones apropiadas para móviles

  • Considerar usar CDN para producción

3. Estructura escalable:

javascript

// Para proyectos grandes

src/

├── data/

│   ├── index.js

│   ├── banners.js

│   ├── moods.js

│   ├── albums.js

│   └── categories.js

├── images/

└── ...

Resumen

Hemos creado un archivo data.js que actúa como una API simulada para nuestra aplicación. Este archivo:

  1. Contiene todas las imágenes organizadas por secciones

  2. Define la estructura de datos para cada componente

  3. Permite fácil mantenimiento y actualización

  4. Prepara el proyecto para futura integración con API real

En el próximo video, crearemos los componentes que consumirán estos datos y darán vida a nuestra interfaz de usuario.

¡Guardamos los cambios y nos vemos en el próximo tutorial


Comentarios

Entradas más populares de este blog

18. Visualizar nuestros componentes limpios con StyleSheet

15. Componente Image

Tema: 23. Justify Content en Flexbox para Column (Columna) en React Nativ