App helper utilities
Reference for a conventional `helper.tsx` (or `utils/helpers`) module in a React Native CLI app—based on React Navigation, AsyncStorage, i18next, dayjs, toasts, and @notifee/react-native. 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 React Native CLI app. Use `npm install` (or your package manager) and make sure required native setup is done for navigation/notifications.
@react-native-async-storage/async-storagedayjs@notifee/react-nativereact-native-toast-message@react-navigation/native
Recommended
npm install @react-native-async-storage/async-storage dayjs @notifee/react-native react-native-toast-message @react-navigation/nativenpm
npm install @react-native-async-storage/async-storage dayjs @notifee/react-native react-native-toast-message @react-navigation/nativePeers react and react-native are provided by your app setup.
Wire in your project
Adjust paths for i18n (`@/src/language-config/...`), `../constants/routes`, `../context/ThemeContext`, and your `../navigation/NavigationService` (`navigationRef`). Optional: set 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); };
React Navigation navigation
- `navigate`
src/utils/helper.tsxReact Navigation `navigate` wrapper using a global `navigationRef`. Passes `params` (and merges `search` into the same params object for convenience).
Uses
- npm: @react-navigation/native
- Your modules: ../navigation/NavigationService (`navigationRef`)
type NavigateOptions = { params?: Record<string, string>; search?: Record<string, string | number | boolean | undefined>; }; export const navigate = ( route: string | any, options?: NavigateOptions ) => { const nav = navigationRef?.current; if (!nav) { devLog("Navigation not ready"); return; } nav.navigate(route as never, { ...(options?.params ?? {}), // React Navigation doesn't have an exact "search" concept like Expo Router. ...(options?.search ?? {}), } as never); };- `navigateBack`
src/utils/helper.tsxCalls `navigationRef.current.goBack()` to pop the navigation stack.
Uses
- npm: @react-navigation/native
- Your modules: ../navigation/NavigationService (`navigationRef`)
export const navigateBack = () => { navigationRef?.current?.goBack(); };- `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, @react-navigation/native
- 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.GOOGLE_API_KEY`, logs and returns `""` if missing; encodes marker pipes per Google’s format.
Uses
- Built-in: 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.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 using **@notifee/react-native**.
Uses
- npm: @notifee/react-native
- React Native: Platform
export const initializeNotifications = async () => { const settings = await notifee.requestPermission(); if (settings.authorizationStatus < 1) { devLog("Permission for notifications not granted!"); return; } if (Platform.OS === "android") { await notifee.createChannel({ id: "default", name: "default", importance: AndroidImportance.HIGH, }); } };- `showNotification`
src/utils/helper.tsxSchedules an immediate local notification (`trigger: null`) with title and body.
Uses
- npm: @notifee/react-native
export const showNotification = async (title: string, body: string) => { await notifee.displayNotification({ title, body, android: { channelId: "default" }, }); };