App helper utilities
Reference for a conventional `helper.tsx` (or `utils/helpers`) module in an Expo / React Native app—based on a common scaffold using Expo Router, AsyncStorage, i18next, dayjs, toasts, and expo-notifications. Names like `saveToSecureStorage` map to AsyncStorage in the sample; swap for secure storage if you handle secrets. Imports such as `@/src/language-config/i18n` are examples—point them at your project.
Packages to install
Install these npm packages in your Expo app (native modules align best with expo install). The downloaded helper.tsx starts with the same list and labels each function.
@react-native-async-storage/async-storagedayjsexpo-deviceexpo-notificationsexpo-routerreact-native-toast-message
Recommended
npx expo install @react-native-async-storage/async-storage dayjs expo-device expo-notifications expo-router react-native-toast-messagenpm
npm install @react-native-async-storage/async-storage dayjs expo-device expo-notifications expo-router react-native-toast-messagePeers react and react-native are included with Expo.
Wire in your project
Adjust paths for i18n (`@/src/language-config/...`), `../constants/routes`, and `../context/ThemeContext`. Optional: set EXPO_PUBLIC_GOOGLE_API_KEY for static maps.
Language & theme
- `changeLanguage`
src/utils/helper.tsxAsync: persists the selected language key (e.g. `@language`) then calls `i18n.changeLanguage` so UI strings update immediately.
Uses
- npm: @react-native-async-storage/async-storage
- Your modules: @/src/language-config/i18n
export const changeLanguage = async (lang: string) => { await saveToSecureStorage("@language", lang); i18n.changeLanguage(lang); };- `getTheme`
src/utils/helper.tsxResolves the effective theme: if the user chose `default`, follows system `ColorSchemeName` (`light` / `dark`); otherwise returns the explicit `Theme` from your context.
Uses
- React Native: ColorSchemeName
- Your modules: ../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; } };
Logging & toasts
- `devLog`
src/utils/helper.tsxWraps `console.log` so logs run only when `__DEV__` is true—keeps release builds quiet.
Uses
- Built-in: __DEV__ (Metro / React Native)
export const devLog = (...args: any[]) => { if (__DEV__) { console.log(...args); } };- `ShowToastOptions`
src/utils/helper.tsxOptional `text1`, `text2`, and `type` (`success` | `error`) passed into `showError` / `showSuccess` when you don’t use a plain string message.
Uses
- Built-in: 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.tsxShows a `react-native-toast-message` toast. Accepts a string or `ShowToastOptions`; merges with `getTextList().somethingWentWrong` when lines are missing.
Uses
- npm: react-native-toast-message
- Your modules: @/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.tsxSame shape as `showError` but defaults to success styling—use for confirmations and happy paths.
Uses
- npm: react-native-toast-message
- Your modules: @/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, }); };
Persistence (AsyncStorage)
- `saveToSecureStorage`
src/utils/helper.tsx`AsyncStorage.setItem` wrapped in try/catch with `devLog` on failure. Despite the name, this sample uses AsyncStorage (not hardware-backed secure storage).
Uses
- 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.tsxReads a string by key; returns `null` if missing or on error.
Uses
- 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` for batch cleanup (logout, toggle remember-me off, etc.).
Uses
- 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.tsxClears the entire AsyncStorage database—use sparingly (full sign-out / reset).
Uses
- 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; } }
Remember me
- `RememberMeCredentials`
src/utils/helper.tsxShape: `email`, `password`, `rememberMe`. Used with the helpers below. Storing passwords in AsyncStorage is convenient for demos but insecure for production—prefer tokens or a secure vault.
Uses
- Built-in: Type-only — no runtime imports
export interface RememberMeCredentials { email: string; password: string; rememberMe: boolean; }- `saveRememberMeCredentials`
src/utils/helper.tsxIf `rememberMe` is true, writes email/password/`@remember_me` keys; otherwise removes those keys.
Uses
- npm: @react-native-async-storage/async-storage
- Note: 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.tsxReads saved credentials when `@remember_me` equals the string true; otherwise returns empty strings and rememberMe: false.
Uses
- 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.tsxRemoves the three remember-me keys without wiping all storage.
Uses
- 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); } };
Strings, dates & currency
- `capitalizeFirstLetter`
src/utils/helper.tsxReturns empty string for falsy input; otherwise uppercases the first character and leaves the rest untouched.
Uses
- Built-in: No packages
export const capitalizeFirstLetter = (text: string): string => { if (!text) return ""; return text.charAt(0).toUpperCase() + text.slice(1); };- `getTimeOfDay`
src/utils/helper.tsxBucketed greeting by hour (5–12 morning, 12–17 afternoon, 17–21 evening, else night) using localized strings from `getTextList()`.
Uses
- Your modules: @/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.tsxFormats a `Date` or date string with **dayjs** (`format` argument, e.g. `YYYY-MM-DD`).
Uses
- npm: dayjs
export const formatDate = (date: string | Date, format: string): string => { return dayjs(date).format(format); };- `formatCurrency`
src/utils/helper.tsx`Intl.NumberFormat` with `style: "currency"`, fixed 2 fraction digits, defaulting currency to `GBP` when omitted.
Uses
- Built-in: 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.tsxMaps ISO codes (USD, EUR, INR, …) to symbols; falls back to `£` when code is empty or unknown.
Uses
- Built-in: 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 & settings
- `openAppSettings`
src/utils/helper.tsxiOS: opens `app-settings:`; Android: `Linking.openSettings()` for app details—use after permission denials.
Uses
- React Native: Linking, Platform
export const openAppSettings = () => { if (Platform.OS === "ios") { Linking.openURL("app-settings:"); } else { Linking.openSettings(); } };- `openWebUrl`
src/utils/helper.tsxThin wrapper around `Linking.openURL` for https links and deep links.
Uses
- React Native: Linking
export const openWebUrl = (url: string) => { Linking.openURL(url); };
Expo Router navigation
- `navigate`
src/utils/helper.tsxTypesafe-ish `router.push` wrapper: `pathname`, optional `params`, and `search` object forwarded to Expo Router.
Uses
- 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.tsxCalls `router.back()` for stack pops.
Uses
- npm: expo-router
export const navigateBack = () => { router.back(); };- `updateUser`
src/utils/helper.tsxPersists access/refresh tokens from an API payload, updates user state via `setUser`, then navigates to a route (or default tab root).
Uses
- npm: @react-native-async-storage/async-storage, expo-router
- Your modules: ../constants/routes · `navigate` in same file
- Note: 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); } };
UI data helpers
- `getConsistentColor`
src/utils/helper.tsxDeterministic pastel color from a string id (e.g. review id) using a simple hash into a fixed palette—good for avatars or tags.
Uses
- Built-in: 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.tsxMarker shape: required `lat`/`lng`, optional `name`, `color`, `label` for Static Maps marker parameters.
Uses
- Built-in: Type-only
export interface MapMarker { name?: string; lat: number; lng: number; color?: string; label?: string; }- `StaticMapOptions`
src/utils/helper.tsxOptions for the Static Maps URL: `center`, `zoom`, `size`, `maptype`, `markers`, `scale`, `format`.
Uses
- Built-in: 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.tsxBuilds a Google Maps Static API URL from options; reads `process.env.EXPO_PUBLIC_GOOGLE_API_KEY`, logs and returns `""` if missing; encodes marker pipes per Google’s format.
Uses
- Built-in: EXPO_PUBLIC_GOOGLE_API_KEY (env), global URL / encodeURIComponent
- Note: 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.tsxConvenience wrapper: centers on one point, adds a red marker, spreads extra `StaticMapOptions` except conflicting fields.
Uses
- Built-in: 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, }); };
Local notifications
- `initializeNotifications`
src/utils/helper.tsxOn real devices, requests notification permission; on Android creates a `default` channel with max importance using **expo-notifications**.
Uses
- 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.tsxSchedules an immediate local notification (`trigger: null`) with title and body.
Uses
- npm: expo-notifications
export const showNotification = async (title: string, body: string) => { await Notifications.scheduleNotificationAsync({ content: { title, body }, trigger: null, }); };