DEV Community

Cover image for Cómo implementar un widget en React Native para iOS
Fernando Arriagada
Fernando Arriagada

Posted on

3 1 1

Cómo implementar un widget en React Native para iOS

Después de implementar widgets en mi último proyecto de React Native con Expo, decidí compartir mi experiencia. 🚀 Los widgets transforman tu app de un simple ícono a una experiencia realmente útil, mostrando información clave directamente en la pantalla de inicio.

En este tutorial, te mostraré los pasos clave para crear widgets para dispositivos iOS usando react-native-shared-group-preferences, la librería que simplifica todo el proceso. ¡Vamos a codear! 💡


🔥 ¿Para qué servirá?

✅ Puedes ver tus próximas tareas sin abrir la app.
✅ Se actualiza automáticamente con la info más reciente.
✅ Es rápido, práctico y queda increíble en la pantalla de inicio.


Paso 1: Crear tu app de React Native con Expo

Note
Si ya tienes creada tu aplicación de React Native con Expo, omite este paso.

  1. Instala la CLI de Expo (si aún no la tienes):
   npm install --global expo-cli
Enter fullscreen mode Exit fullscreen mode

Esta herramienta te permitirá crear y administrar proyectos de Expo fácilmente.

  1. Crea un nuevo proyecto con Expo:
   expo init MyNewApp
Enter fullscreen mode Exit fullscreen mode

Elige la plantilla que más te guste (por ejemplo, la “blank”) y espera a que se instalen todas las dependencias.

  1. Navega dentro de la carpeta del proyecto y ejecuta tu app:
   cd MyNewApp
   expo start
Enter fullscreen mode Exit fullscreen mode

Se abrirá la interfaz de Expo en tu navegador. Desde ahí, puedes escanear el código QR con la app de Expo Go en tu dispositivo o ejecutar la aplicación en un emulador.

  1. ¡Listo! Ya tienes una app de React Native con Expo corriendo. Siéntete libre de hacer un “Hello World” para comprobar que todo está en orden.

Paso 2: Preparando tu proyecto para widgets en iOS

Si estás usando Expo o necesitas generar la carpeta de iOS desde cero, npx expo prebuild es tu mejor amigo. Este comando no solo crea la carpeta ./ios, sino que también configura todos los archivos nativos necesarios con la magia de Expo.

# Ejecuta este comando en la raíz de tu proyecto
npx expo prebuild --platform ios
Enter fullscreen mode Exit fullscreen mode

Note
prebuild regenerará completamente tu carpeta ios, así que si tienes configuraciones personalizadas, deberás readaptarlas después.

Paso 3: Instalar react-native-shared-group-preferences

Luego, instala la librería que nos permitirá compartir datos entre nuestra aplicación React Native y el widget iOS:

npm install react-native-shared-group-preferences --save
# o con yarn
yarn add react-native-shared-group-preferences
Enter fullscreen mode Exit fullscreen mode

Después, instala los pods necesarios:

cd ios && pod install
# o
npx pod-install
Enter fullscreen mode Exit fullscreen mode

Note
Después de instalar cualquier librería de npm se debe ejecutar pod install.

Paso 4: Configurar App Groups en Xcode

1. Abre tu proyecto en Xcode (`ios/TuProyecto.xcworkspace`).  
2. Selecciona tu proyecto en el navegador de proyectos.  
3. Selecciona la pestaña **Signing & Capabilities**.  
4. Haz clic en el botón **+** para añadir una nueva capacidad.  
5. Añade **App Groups**.  
6. Crea un nuevo grupo con el formato `group.com.tudominio.tuapp`.  
7. Asegúrate de que tu App ID tiene habilitada esta capacidad en el portal de desarrolladores de Apple.
Enter fullscreen mode Exit fullscreen mode

Paso 5: Crear una extensión de widget en Xcode

1. En Xcode, ve a **File > New > Target**.  
2. Selecciona **Widget Extension** de la sección "iOS".  
3. Nombra tu widget (por ejemplo, **MyAppWidget**).  
4. Selecciona **Include Configuration Intent** si deseas que tu widget sea configurable.  
5. Asegúrate de que **Group** esté configurado para el mismo App Group que configuraste previamente.
Enter fullscreen mode Exit fullscreen mode

Paso 6: Configurar tu widget en Swift

Note
Al crear un widget con Xcode, se generan diferentes archivos swift dentro de la carpeta ./ios/MyAppWidget, para simplificar la guía solo vamos dejar el archivo MyAppWidget.swift

Modifica el archivo MyAppWidget.swift (anteriormente MyAppWidget.swift) generado:

import WidgetKit
import SwiftUI

struct Reminder: Codable, Identifiable {
    var id: Int
    var name: String
}

struct ReminderWidgetEntry: TimelineEntry {
    let date: Date
    let reminders: [Reminder]
}

struct ReminderWidgetProvider: TimelineProvider {
    func placeholder(in context: Context) -> ReminderWidgetEntry {
        ReminderWidgetEntry(date: Date(), reminders: [Reminder(id: 0, name: "Sample Reminder")])
    }

    func getSnapshot(in context: Context, completion: @escaping (ReminderWidgetEntry) -> ()) {
        let entry = ReminderWidgetEntry(date: Date(), reminders: loadReminders())
        completion(entry)
    }

    func getTimeline(in context: Context, completion: @escaping (Timeline<ReminderWidgetEntry>) -> ()) {        
        let entry = ReminderWidgetEntry(date: Date(), reminders: loadReminders())
        let timeline = Timeline(entries: [entry], policy: .after(Date().addingTimeInterval(900)))
        completion(timeline)
    }

    func loadReminders() -> [Reminder] {
        let sharedDefaults = UserDefaults(suiteName: "group.com.tudominio.tuapp")
        if let jsonString = sharedDefaults?.string(forKey: "reminders"),
           let data = jsonString.data(using: .utf8),
           let reminders = try? JSONDecoder().decode([Reminder].self, from: data) {
            return reminders
        }
        return []
    }
}

struct MyAppWidgetEntryView: View {
    var entry: ReminderWidgetProvider.Entry

    var body: some View {
        VStack(alignment: .leading) {
            Text("My Reminders")
                .font(.headline)
            if entry.reminders.isEmpty {
                Text("No reminders")
                    .font(.caption)
            } else {
                ForEach(entry.reminders) { reminder in
                    Text(reminder.name)
                        .font(.body)
                        .lineLimit(1)
                }
            }
        }
        .padding()
    }
}

@main
struct MyAppWidget: Widget {
    let kind: String = "MyAppWidget"

    var body: some WidgetConfiguration {
        StaticConfiguration(kind: kind, provider: ReminderWidgetProvider()) { entry in
            MyAppWidgetEntryView(entry: entry)
        }
        .configurationDisplayName("Widget Reminder Calendar")
        .description("Displays a list of your reminders.")
    }
}

struct MyAppWidget_Previews: PreviewProvider {
    static var previews: some View {
        let entry = ReminderWidgetEntry(date: Date(), reminders: [
            Reminder(id: 1, name: "Reminder A"),
            Reminder(id: 2, name: "Reminder B"),
            Reminder(id: 3, name: "Reminder C")
        ])
        MyAppWidgetEntryView(entry: entry)
            .previewContext(WidgetPreviewContext(family: .systemMedium))
    }
}
Enter fullscreen mode Exit fullscreen mode

Paso 7: Compartir datos desde React Native

Ahora, modifica tu aplicación React Native para compartir datos con el widget:

import React from 'react';
import { View, Text, Button, StyleSheet } from 'react-native';
import SharedGroupPreferences from 'react-native-shared-group-preferences';

const APP_GROUP_IDENTIFIER = 'group.com.tudominio.tuapp';

export default function UpdateWidget() {
  const updateWidgetData = async () => {
    const data = [
      { id: 1, name: 'Reminder A' },
      { id: 2, name: 'Reminder B' },
    ];

    await SharedGroupPreferences.setItem(
      'reminders',
      JSON.stringify(data),
      APP_GROUP_IDENTIFIER
    );
  };

  return (
    <View style={styles.container}>
      <Text style={styles.title}>Update Widget</Text>
      <Button 
        title="Update Widget Now" 
        onPress={updateWidgetData} 
      />
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff',
    alignItems: 'center',
    justifyContent: 'center'
  },
  title: {
    fontSize: 18,
    marginBottom: 8
  }
});
Enter fullscreen mode Exit fullscreen mode

Paso 8: Ejecutar en emulador 🚀

  1. En Xcode, en la pestaña superior selecciona el Target principal y el emulador del dispositivo.
  2. En Xcode, ve a Product > Run.
  3. ¡Listo! Si no tuviste errores, deberías poder agregar un widget con los datos sincronizados de tu aplicación como en la siguiente imagen:

React Native Widget

Conclusión

¿Listo para darle un superpoder a tu app? 🚀 Los widgets te simplifican la vida y te muestran información esencial sin tener que abrir la aplicación. Con React Native y react-native-shared-group-preferences, convertir tu app en una experiencia de pantalla de inicio es más fácil de lo que imaginas.

Sí, tendrás que ensuciarte un poco con Swift, pero el resultado final vale cada línea de código.

A codear ese widget!🔥🔥

Si tienes dudas, no dudes en preguntar 🤘

AWS Q Developer image

Your AI Code Assistant

Generate and update README files, create data-flow diagrams, and keep your project fully documented. Built to handle large projects, Amazon Q Developer works alongside you from idea to production code.

Get started free in your IDE

Top comments (0)

A Workflow Copilot. Tailored to You.

Pieces.app image

Our desktop app, with its intelligent copilot, streamlines coding by generating snippets, extracting code from screenshots, and accelerating problem-solving.

Read the docs

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay