or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

app-utilities.mderror-handling.mdevent-communication.mdindex.mdnative-modules.mdnative-views.mdpermissions.mdplatform-utilities.mdshared-memory.mduuid-generation.md
tile.json

app-utilities.mddocs/

App Utilities

Additional utility functions for application lifecycle management and React development helpers.

Capabilities

App Reload Functionality

Function to programmatically reload the application using the same JavaScript bundle.

/**
 * Reloads the app using the same JavaScript bundle currently running
 * Works for both release and debug builds
 * Unlike Updates.reloadAsync(), does not use new updates
 * @param reason - Optional reason for reloading (used on some platforms)
 * @returns Promise that resolves when reload is initiated
 */
function reloadAppAsync(reason?: string): Promise<void>;

Usage Examples:

import { reloadAppAsync } from "expo-modules-core";

// Basic app reload
async function handleAppReload() {
  try {
    await reloadAppAsync("User requested reload");
    // This code may not execute as app is reloading
  } catch (error) {
    console.error("Failed to reload app:", error);
  }
}

// Reload with user confirmation
async function confirmAndReload(reason: string) {
  const shouldReload = confirm(`Reload app? Reason: ${reason}`);
  if (shouldReload) {
    await reloadAppAsync(reason);
  }
}

// Reload after critical error
class ErrorBoundary extends Component {
  state = { hasError: false };
  
  static getDerivedStateFromError(error: Error) {
    return { hasError: true };
  }
  
  componentDidCatch(error: Error, errorInfo: any) {
    console.error("Critical error caught:", error, errorInfo);
  }
  
  handleReload = async () => {
    await reloadAppAsync("Recovery from critical error");
  };
  
  render() {
    if (this.state.hasError) {
      return (
        <div>
          <h2>Something went wrong</h2>
          <button onClick={this.handleReload}>
            Reload App
          </button>
        </div>
      );
    }
    
    return this.props.children;
  }
}

// Scheduled reload (e.g., after configuration changes)
class ConfigurationManager {
  private reloadTimeout?: NodeJS.Timeout;
  
  scheduleReload(delayMs: number, reason: string) {
    if (this.reloadTimeout) {
      clearTimeout(this.reloadTimeout);
    }
    
    this.reloadTimeout = setTimeout(async () => {
      console.log(`Reloading app in response to: ${reason}`);
      await reloadAppAsync(reason);
    }, delayMs);
  }
  
  cancelScheduledReload() {
    if (this.reloadTimeout) {
      clearTimeout(this.reloadTimeout);
      this.reloadTimeout = undefined;
    }
  }
}

// Usage
const configManager = new ConfigurationManager();

// Reload after 5 seconds due to config change
configManager.scheduleReload(5000, "Configuration updated");

// Cancel if user takes action
configManager.cancelScheduledReload();

React Development Helpers

Utilities specifically designed for React development and testing scenarios.

/**
 * Create a React ref object that is friendly for snapshots
 * Represented as [React.ref] in snapshots instead of complex object
 * @returns React ref object for component instances
 */
function createSnapshotFriendlyRef<T>(): RefObject<T | null>;

Usage Examples:

import { createSnapshotFriendlyRef } from "expo-modules-core";
import { useEffect } from "react";

// Component with snapshot-friendly ref
function VideoPlayer({ source }: { source: string }) {
  const videoRef = createSnapshotFriendlyRef<HTMLVideoElement>();
  
  useEffect(() => {
    const video = videoRef.current;
    if (video) {
      video.src = source;
      video.load();
    }
  }, [source]);
  
  const handlePlay = () => {
    videoRef.current?.play();
  };
  
  const handlePause = () => {
    videoRef.current?.pause();
  };
  
  return (
    <div>
      <video ref={videoRef} controls />
      <div>
        <button onClick={handlePlay}>Play</button>
        <button onClick={handlePause}>Pause</button>
      </div>
    </div>
  );
}

// Testing component with snapshot
import { render } from '@testing-library/react';

test('VideoPlayer snapshot', () => {
  const { container } = render(<VideoPlayer source="video.mp4" />);
  
  // The ref will appear as [React.ref] in snapshot instead of complex object
  expect(container).toMatchSnapshot();
});

// Multiple refs in a component
function MediaController() {
  const audioRef = createSnapshotFriendlyRef<HTMLAudioElement>();
  const videoRef = createSnapshotFriendlyRef<HTMLVideoElement>();
  const canvasRef = createSnapshotFriendlyRef<HTMLCanvasElement>();
  
  const syncPlayback = () => {
    const audio = audioRef.current;
    const video = videoRef.current;
    
    if (audio && video) {
      audio.currentTime = video.currentTime;
      audio.play();
      video.play();
    }
  };
  
  const drawVisualization = () => {
    const canvas = canvasRef.current;
    const audio = audioRef.current;
    
    if (canvas && audio) {
      const ctx = canvas.getContext('2d');
      // Draw audio visualization
    }
  };
  
  return (
    <div>
      <video ref={videoRef} />
      <audio ref={audioRef} />
      <canvas ref={canvasRef} />
      <button onClick={syncPlayback}>Sync & Play</button>
      <button onClick={drawVisualization}>Visualize</button>
    </div>
  );
}

Development and Debugging Utilities

Helper functions for development and debugging scenarios.

import { reloadAppAsync, Platform } from "expo-modules-core";

// Development-only reload functionality
class DevTools {
  static isDevMode(): boolean {
    return __DEV__ === true;
  }
  
  static async enableHotReload() {
    if (!this.isDevMode()) {
      console.warn("Hot reload only available in development mode");
      return;
    }
    
    // Setup hot reload triggers
    if (Platform.canUseEventListeners) {
      window.addEventListener('keydown', (event) => {
        if (event.ctrlKey && event.key === 'r') {
          event.preventDefault();
          this.reloadApp("Hot reload shortcut");
        }
      });
    }
  }
  
  static async reloadApp(reason: string) {
    if (this.isDevMode()) {
      console.log(`[DevTools] Reloading: ${reason}`);
      await reloadAppAsync(reason);
    } else {
      console.warn("Reload not allowed in production");
    }
  }
  
  static createErrorHandler() {
    return (error: Error, errorInfo: any) => {
      console.error("[DevTools] Error:", error);
      console.error("[DevTools] Error Info:", errorInfo);
      
      if (this.isDevMode()) {
        // In development, offer to reload
        setTimeout(() => {
          if (confirm("An error occurred. Reload the app?")) {
            this.reloadApp("Error recovery");
          }
        }, 1000);
      }
    };
  }
}

// Usage in development
if (__DEV__) {
  DevTools.enableHotReload();
}

// Error boundary with dev tools integration
class DevErrorBoundary extends Component {
  state = { hasError: false, error: null };
  
  static getDerivedStateFromError(error: Error) {
    return { hasError: true, error };
  }
  
  componentDidCatch(error: Error, errorInfo: any) {
    const handleError = DevTools.createErrorHandler();
    handleError(error, errorInfo);
  }
  
  render() {
    if (this.state.hasError) {
      return (
        <div>
          <h2>Development Error</h2>
          {__DEV__ && (
            <details>
              <summary>Error Details</summary>
              <pre>{this.state.error?.stack}</pre>
            </details>
          )}
          <button onClick={() => DevTools.reloadApp("Manual error recovery")}>
            Reload App
          </button>
        </div>
      );
    }
    
    return this.props.children;
  }
}

Application Lifecycle Patterns

Common patterns for managing application state and lifecycle events.

import { reloadAppAsync, Platform } from "expo-modules-core";

// Application state manager
class AppStateManager {
  private static instance: AppStateManager;
  private listeners: Array<(event: AppEvent) => void> = [];
  
  static getInstance(): AppStateManager {
    if (!this.instance) {
      this.instance = new AppStateManager();
    }
    return this.instance;
  }
  
  onAppEvent(listener: (event: AppEvent) => void) {
    this.listeners.push(listener);
    return () => {
      const index = this.listeners.indexOf(listener);
      if (index >= 0) {
        this.listeners.splice(index, 1);
      }
    };
  }
  
  private notifyListeners(event: AppEvent) {
    this.listeners.forEach(listener => {
      try {
        listener(event);
      } catch (error) {
        console.error("Error in app event listener:", error);
      }
    });
  }
  
  async reloadForUpdate(updateInfo: UpdateInfo) {
    this.notifyListeners({ type: 'reload_starting', updateInfo });
    
    try {
      await reloadAppAsync(`Update available: ${updateInfo.version}`);
    } catch (error) {
      this.notifyListeners({ type: 'reload_failed', error });
    }
  }
  
  async reloadForError(error: Error) {
    this.notifyListeners({ type: 'error_reload', error });
    
    // Wait a bit to let user see the error
    setTimeout(async () => {
      await reloadAppAsync(`Error recovery: ${error.message}`);
    }, 2000);
  }
}

type AppEvent = 
  | { type: 'reload_starting'; updateInfo: UpdateInfo }
  | { type: 'reload_failed'; error: Error }
  | { type: 'error_reload'; error: Error };

type UpdateInfo = {
  version: string;
  description: string;
};

// Usage in app root
function App() {
  const appStateManager = AppStateManager.getInstance();
  
  useEffect(() => {
    const unsubscribe = appStateManager.onAppEvent((event) => {
      switch (event.type) {
        case 'reload_starting':
          // Show loading indicator
          console.log("App reloading for update:", event.updateInfo.version);
          break;
        case 'reload_failed':
          // Show error message
          console.error("Reload failed:", event.error);
          break;
        case 'error_reload':
          // Show error recovery message
          console.log("Reloading due to error:", event.error.message);
          break;
      }
    });
    
    return unsubscribe;
  }, []);
  
  return (
    <DevErrorBoundary>
      <MainApplication />
    </DevErrorBoundary>
  );
}

Types

/**
 * React ref object type for snapshot-friendly refs
 */
interface RefObject<T> {
  readonly current: T | null;
}

/**
 * Application event types for lifecycle management
 */
type AppEvent = {
  type: string;
  [key: string]: any;
};