Utilidades helper de la app

Referencia de un `helper.tsx` típico (o `utils/helpers`) en Expo/React Native, basado en un esquema común con Expo Router, AsyncStorage, i18next, dayjs, toasts y expo-notifications. Nombres como `saveToSecureStorage` aquí usan AsyncStorage; cámbialo por almacenamiento seguro si manejas secretos. Rutas como `@/src/language-config/i18n` son ejemplos: adáptalas a tu proyecto.

Paquetes a instalar

Instala estos paquetes npm en tu app Expo (expo install suele ir mejor con módulos nativos). El helper.tsx descargado incluye la misma lista y anota cada función.

  • @react-native-async-storage/async-storage
  • dayjs
  • expo-device
  • expo-notifications
  • expo-router
  • react-native-toast-message

Recomendado

npx expo install @react-native-async-storage/async-storage dayjs expo-device expo-notifications expo-router react-native-toast-message

npm

npm install @react-native-async-storage/async-storage dayjs expo-device expo-notifications expo-router react-native-toast-message

react y react-native ya vienen con Expo.

Conecta en tu proyecto

Ajusta rutas de i18n (`@/src/language-config/...`), `../constants/routes` y `../context/ThemeContext`. Opcional: EXPO_PUBLIC_GOOGLE_API_KEY para mapas estáticos.

Incluye cabecera de dependencias, imports y comentarios /** … */ por export (npm / React Native / tus módulos / integrado).

Idioma y tema

`changeLanguage`

src/utils/helper.tsx

Async: guarda la clave de idioma (p. ej. `@language`) y llama `i18n.changeLanguage` para refrescar strings.

Usa

  • npm: @react-native-async-storage/async-storage
  • Tus módulos: @/src/language-config/i18n
export const changeLanguage = async (lang: string) => {
  await saveToSecureStorage("@language", lang);
  i18n.changeLanguage(lang);
};
`getTheme`

src/utils/helper.tsx

Resuelve el tema efectivo: si el usuario eligió `default`, sigue el `ColorSchemeName` del sistema; si no, devuelve el `Theme` explícito del contexto.

Usa

  • React Native: ColorSchemeName
  • Tus módulos: ../context/ThemeContext (`Theme`)
export const getTheme = (theme: Theme, systemTheme: ColorSchemeName) => {
  if (theme === "default" && systemTheme === "dark") {
    return "dark";
  } else if (theme === "default" && systemTheme === "light") {
    return "light";
  } else {
    return theme;
  }
};

Logs y toasts

`devLog`

src/utils/helper.tsx

Envuelve `console.log` para que solo registre cuando `__DEV__` es verdadero.

Usa

  • Integrado: __DEV__ (Metro / React Native)
export const devLog = (...args: any[]) => {
  if (__DEV__) {
    console.log(...args);
  }
};
`ShowToastOptions`

src/utils/helper.tsx

Campos opcionales `text1`, `text2` y `type` (success | error) para `showError` / `showSuccess` cuando no pasas solo un string.

Usa

  • Integrado: Type-only — pair with react-native-toast-message in showError / showSuccess
export interface ShowToastOptions {
  text1?: string;
  text2?: string;
  type?: "success" | "error";
}
`showError`

src/utils/helper.tsx

Muestra un toast de react-native-toast-message. Acepta string u opciones; rellena con `getTextList().somethingWentWrong` si faltan textos.

Usa

  • npm: react-native-toast-message
  • Tus módulos: @/src/language-config/TextList (`getTextList`)
export const showError = (messageOrOptions: string | ShowToastOptions) => {
  let type = "error";
  let text1 = "";
  let text2 = getTextList().somethingWentWrong;

  if (typeof messageOrOptions === "string") {
    text2 = messageOrOptions || text2;
  } else {
    type = messageOrOptions.type || type;
    text1 = messageOrOptions.text1 || text1;
    text2 = messageOrOptions.text2 || text2;
  }

  Toast.show({
    type,
    text1: `${text1}`,
    text2,
  });
};
`showSuccess`

src/utils/helper.tsx

Igual que `showError` pero orientado a éxito (confirmaciones).

Usa

  • npm: react-native-toast-message
  • Tus módulos: @/src/language-config/TextList (`getTextList`)
export const showSuccess = (messageOrOptions: string | ShowToastOptions) => {
  let type = "success";
  let text1 = "";
  let text2 = getTextList().somethingWentWrong;

  if (typeof messageOrOptions === "string") {
    text2 = messageOrOptions || text2;
  } else {
    type = messageOrOptions.type || type;
    text1 = messageOrOptions.text1 || text1;
    text2 = messageOrOptions.text2 || text2;
  }

  Toast.show({
    type,
    text1: `${text1}`,
    text2,
  });
};

Persistencia (AsyncStorage)

`saveToSecureStorage`

src/utils/helper.tsx

`AsyncStorage.setItem` con try/catch y `devLog`. El nombre sugiere secure, pero el ejemplo usa AsyncStorage.

Usa

  • npm: @react-native-async-storage/async-storage
export async function saveToSecureStorage(
  key: string,
  value: string
): Promise<void> {
  try {
    await AsyncStorage.setItem(key, value);
  } catch (error) {
    devLog("[AsyncStorage] Error saving data:", error);
  }
}
`getFromSecureStorage`

src/utils/helper.tsx

Lee un string por clave; devuelve `null` si no hay valor o hay error.

Usa

  • npm: @react-native-async-storage/async-storage
export async function getFromSecureStorage(
  key: string
): Promise<string | null> {
  try {
    const value = await AsyncStorage.getItem(key);
    return value;
  } catch (error) {
    devLog("[AsyncStorage] Error retrieving data:", error);
    return null;
  }
}
`removeKeysFromSecureStorage`

src/utils/helper.tsx

`AsyncStorage.multiRemove` para borrar varias claves (logout, desactivar recordarme…).

Usa

  • npm: @react-native-async-storage/async-storage
export async function removeKeysFromSecureStorage(key: string[]) {
  try {
    await AsyncStorage.multiRemove(key);
  } catch (error) {
    devLog("[AsyncStorage] Error removing data:", error);
    return null;
  }
}
`removeAllKeysFromSecureStorage`

src/utils/helper.tsx

Vacía todo AsyncStorage—solo para reset fuerte o cierre de sesión global.

Usa

  • npm: @react-native-async-storage/async-storage
export async function removeAllKeysFromSecureStorage() {
  try {
    await AsyncStorage.clear();
  } catch (error) {
    devLog("[AsyncStorage] Error removing all data:", error);
    return null;
  }
}

Recordarme

`RememberMeCredentials`

src/utils/helper.tsx

Forma: `email`, `password`, `rememberMe`. Guardar contraseñas en AsyncStorage es arriesgado en producción; mejor tokens o almacén seguro.

Usa

  • Integrado: Type-only — no runtime imports
export interface RememberMeCredentials {
  email: string;
  password: string;
  rememberMe: boolean;
}
`saveRememberMeCredentials`

src/utils/helper.tsx

Si `rememberMe` es true, guarda email/contraseña/`@remember_me`; si no, borra esas claves.

Usa

  • npm: @react-native-async-storage/async-storage
  • Nota: Uses saveToSecureStorage / removeKeysFromSecureStorage
export const saveRememberMeCredentials = async (credentials: RememberMeCredentials): Promise<void> => {
  try {
    if (credentials.rememberMe) {
      await saveToSecureStorage("@remembered_email", credentials.email);
      await saveToSecureStorage("@remembered_password", credentials.password);
      await saveToSecureStorage("@remember_me", "true");
    } else {
      await removeKeysFromSecureStorage([
        "@remembered_email",
        "@remembered_password",
        "@remember_me",
      ]);
    }
  } catch (error) {
    devLog("[Remember Me] Error saving credentials:", error);
  }
};
`loadRememberMeCredentials`

src/utils/helper.tsx

Lee credenciales si `@remember_me` vale la cadena true; si no, devuelve vacío y rememberMe false.

Usa

  • npm: @react-native-async-storage/async-storage
export const loadRememberMeCredentials = async (): Promise<RememberMeCredentials> => {
  try {
    const savedEmail = await getFromSecureStorage("@remembered_email");
    const savedPassword = await getFromSecureStorage("@remembered_password");
    const rememberMe = await getFromSecureStorage("@remember_me");

    if (savedEmail && savedPassword && rememberMe === "true") {
      return {
        email: savedEmail,
        password: savedPassword,
        rememberMe: true,
      };
    }

    return {
      email: "",
      password: "",
      rememberMe: false,
    };
  } catch (error) {
    devLog("[Remember Me] Error loading credentials:", error);
    return {
      email: "",
      password: "",
      rememberMe: false,
    };
  }
};
`clearRememberMeCredentials`

src/utils/helper.tsx

Elimina solo las tres claves de recordarme.

Usa

  • npm: @react-native-async-storage/async-storage
export const clearRememberMeCredentials = async (): Promise<void> => {
  try {
    await removeKeysFromSecureStorage([
      "@remembered_email",
      "@remembered_password",
      "@remember_me",
    ]);
  } catch (error) {
    devLog("[Remember Me] Error clearing credentials:", error);
  }
};

Texto, fechas y moneda

`capitalizeFirstLetter`

src/utils/helper.tsx

Devuelve cadena vacía si no hay texto; si no, mayúscula inicial y resto igual.

Usa

  • Integrado: No packages
export const capitalizeFirstLetter = (text: string): string => {
  if (!text) return "";
  return text.charAt(0).toUpperCase() + text.slice(1);
};
`getTimeOfDay`

src/utils/helper.tsx

Saludo por franja horaria (mañana/tarde/tarde-noche/noche) con strings localizados de `getTextList()`.

Usa

  • Tus módulos: @/src/language-config/TextList (`getTextList`)
export const getTimeOfDay = () => {
  const currentHour = new Date().getHours();

  if (currentHour >= 5 && currentHour < 12) {
    return getTextList().morning;
  } else if (currentHour >= 12 && currentHour < 17) {
    return getTextList().afternoon;
  } else if (currentHour >= 17 && currentHour < 21) {
    return getTextList().evening;
  } else {
    return getTextList().night;
  }
};
`formatDate`

src/utils/helper.tsx

Formatea `Date` o string con dayjs según el patrón (ej. YYYY-MM-DD).

Usa

  • npm: dayjs
export const formatDate = (date: string | Date, format: string): string => {
  return dayjs(date).format(format);
};
`formatCurrency`

src/utils/helper.tsx

`Intl.NumberFormat` en modo moneda con 2 decimales; moneda por defecto GBP si falta.

Usa

  • Integrado: Intl.NumberFormat
export const formatCurrency = (amount: number, currency: string) => {
  return new Intl.NumberFormat("en-US", {
    style: "currency",
    currency: currency || "GBP",
    minimumFractionDigits: 2,
    maximumFractionDigits: 2,
  }).format(amount);
};
`getCurrencySymbol`

src/utils/helper.tsx

Mapea códigos ISO a símbolos; si falta o es desconocido, cae en £.

Usa

  • Integrado: Intl (standard JS)
export const getCurrencySymbol = (currencyCode: string | null | undefined): string => {
  const normalizedCurrencyCode = currencyCode?.trim().toUpperCase();

  if (!normalizedCurrencyCode) {
    return "£";
  }

  const currencyMap: Record<string, string> = {
    USD: "$",
    GBP: "£",
    EUR: "€",
    INR: "₹",
    JPY: "¥",
    CNY: "¥",
    AUD: "A$",
    CAD: "C$",
    CHF: "CHF",
    SEK: "kr",
    NOK: "kr",
    DKK: "kr",
    PLN: "zł",
    RUB: "₽",
    BRL: "R$",
    MXN: "$",
    ZAR: "R",
    SGD: "S$",
    HKD: "HK$",
    NZD: "NZ$",
    KRW: "₩",
    TRY: "₺",
    AED: "د.إ",
    SAR: "﷼",
    PKR: "₨",
    BDT: "৳",
    THB: "฿",
    MYR: "RM",
    IDR: "Rp",
    PHP: "₱",
    VND: "₫",
  };

  return currencyMap[normalizedCurrencyCode] || "£";
};

Linking y ajustes

`openAppSettings`

src/utils/helper.tsx

iOS: URL `app-settings:`; Android: `Linking.openSettings()` (pantalla de la app).

Usa

  • React Native: Linking, Platform
export const openAppSettings = () => {
  if (Platform.OS === "ios") {
    Linking.openURL("app-settings:");
  } else {
    Linking.openSettings();
  }
};
`openWebUrl`

src/utils/helper.tsx

Wrapper de `Linking.openURL` para URLs web o deep links.

Usa

  • React Native: Linking
export const openWebUrl = (url: string) => {
  Linking.openURL(url);
};

Navegación con Expo Router

`navigate`

src/utils/helper.tsx

Envoltorio de `router.push`: `pathname`, `params` opcional y objeto `search` para Expo Router.

Usa

  • npm: expo-router
type RelativePathString = string;

type NavigateOptions = {
  params?: Record<string, string>;
  search?: Record<string, string | number | boolean | undefined>;
};

export const navigate = (
  route: RelativePathString | any,
  options?: NavigateOptions
) => {
  router.push({
    pathname: route,
    ...(options?.params && { params: options.params }),
    ...(options?.search && { search: options.search }),
  });
};
`navigateBack`

src/utils/helper.tsx

Llama `router.back()` para volver en la pila.

Usa

  • npm: expo-router
export const navigateBack = () => {
  router.back();
};
`updateUser`

src/utils/helper.tsx

Guarda tokens de acceso/refresh, actualiza usuario con `setUser` y navega a una ruta o al root de tabs.

Usa

  • npm: @react-native-async-storage/async-storage, expo-router
  • Tus módulos: ../constants/routes · `navigate` in same file
  • Nota: Expects API shape with data.access_token, data.refresh_token, data.user
export const updateUser = async (
  data: any,
  setUser: (user: any) => void,
  route?: any
) => {
  await saveToSecureStorage("@access_token", data?.data?.access_token);
  await saveToSecureStorage("@refresh_token", data?.data?.refresh_token);
  setUser(data?.data?.user);
  if (route) {
    navigate(route);
  } else {
    navigate(Routes.TABS.ROOT);
  }
};

Datos para UI

`getConsistentColor`

src/utils/helper.tsx

Color pastel estable a partir de un id (hash simple sobre una paleta fija)—avatars o etiquetas.

Usa

  • Integrado: No packages
export const getConsistentColor = (reviewId: string) => {
  const colors = [
    "#FF6B6B",
    "#4ECDC4",
    "#45B7D1",
    "#96CEB4",
    "#FFEAA7",
    "#DDA0DD",
    "#98D8C8",
    "#F7DC6F",
    "#BB8FCE",
    "#85C1E9",
    "#F8C471",
    "#82E0AA",
    "#F1948A",
    "#85C1E9",
    "#D7BDE2",
  ];

  let hash = 0;
  for (let i = 0; i < reviewId.length; i++) {
    hash = reviewId.charCodeAt(i) + ((hash << 5) - hash);
  }
  return colors[Math.abs(hash) % colors.length];
};

Google Static Maps

`MapMarker`

src/utils/helper.tsx

Marcador: `lat`/`lng` obligatorios; `name`, `color`, `label` opcionales para Static Maps.

Usa

  • Integrado: Type-only
export interface MapMarker {
  name?: string;
  lat: number;
  lng: number;
  color?: string;
  label?: string;
}
`StaticMapOptions`

src/utils/helper.tsx

Opciones de URL estática: centro, zoom, tamaño, tipo de mapa, marcadores, escala, formato.

Usa

  • Integrado: Type-only
export interface StaticMapOptions {
  center?: { lat: number; lng: number };
  zoom?: number;
  size?: string;
  maptype?: "roadmap" | "satellite" | "terrain" | "hybrid";
  markers?: MapMarker[];
  scale?: 1 | 2;
  format?: "png" | "jpg" | "gif";
}
`generateStaticMapUrl`

src/utils/helper.tsx

Construye URL de Google Static Maps; usa `EXPO_PUBLIC_GOOGLE_API_KEY`; si falta, devuelve cadena vacía y registra aviso.

Usa

  • Integrado: EXPO_PUBLIC_GOOGLE_API_KEY (env), global URL / encodeURIComponent
  • Nota: Uses Google Maps Static API
export const generateStaticMapUrl = (options: StaticMapOptions): string => {
  const {
    center,
    zoom = 13,
    size = "600x200",
    maptype = "roadmap",
    markers = [],
    scale = 1,
    format = "png",
  } = options;

  const apiKey = process.env.EXPO_PUBLIC_GOOGLE_API_KEY;

  if (!apiKey) {
    devLog("Warning: Google Maps API key not found");
    return "";
  }

  const baseUrl = "https://maps.googleapis.com/maps/api/staticmap";
  const params: string[] = [];

  if (center) {
    params.push(`center=${center.lat},${center.lng}`);
  } else if (markers.length > 0) {
    params.push(`center=${markers[0].lat},${markers[0].lng}`);
  }

  params.push(`zoom=${zoom}`);
  params.push(`size=${size}`);
  params.push(`maptype=${maptype}`);
  params.push(`scale=${scale}`);
  params.push(`format=${format}`);

  if (markers.length > 0) {
    markers.forEach((marker) => {
      const markerParams: string[] = [];
      if (marker.color) {
        markerParams.push(`color:${marker.color}`);
      }
      if (marker.label) {
        markerParams.push(`label:${marker.label}`);
      }
      markerParams.push(`${marker.lat},${marker.lng}`);
      const markerString = markerParams.join("|");
      params.push(`markers=${encodeURIComponent(markerString)}`);
    });
  }

  params.push(`key=${apiKey}`);

  const finalUrl = `${baseUrl}?${params.join("&")}`;
  devLog("Generated Static Map URL:", finalUrl);
  return finalUrl;
};
`generateStaticMapUrlWithMarker`

src/utils/helper.tsx

Atajo con un marcador rojo centrado y el resto de opciones.

Usa

  • Integrado: Builds on generateStaticMapUrl
export const generateStaticMapUrlWithMarker = (
  name: string,
  lat: number,
  lng: number,
  options?: Omit<StaticMapOptions, "markers" | "center">
): string => {
  return generateStaticMapUrl({
    center: { lat, lng },
    markers: [{ name, lat, lng, color: "red" }],
    ...options,
  });
};

Notificaciones locales

`initializeNotifications`

src/utils/helper.tsx

En dispositivo real pide permiso; en Android crea canal default con importancia máxima (expo-notifications).

Usa

  • npm: expo-notifications, expo-device
  • React Native: Platform
export const initializeNotifications = async () => {
  if (Device.isDevice) {
    const { status } = await Notifications.requestPermissionsAsync();
    if (status !== "granted") {
      devLog("Permission for notifications not granted!");
      return;
    }
  }

  if (Platform.OS === "android") {
    await Notifications.setNotificationChannelAsync("default", {
      name: "default",
      importance: Notifications.AndroidImportance.MAX,
    });
  }
};
`showNotification`

src/utils/helper.tsx

Programa notificación local inmediata (`trigger: null`) con título y cuerpo.

Usa

  • npm: expo-notifications
export const showNotification = async (title: string, body: string) => {
  await Notifications.scheduleNotificationAsync({
    content: { title, body },
    trigger: null,
  });
};

Patrocinado

Promoción breve