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

event-communication.mddocs/

Event Communication

Native-backed event system for real-time communication between JavaScript and native code, with full TypeScript support and automatic lifecycle management.

Capabilities

EventEmitter Class

A native-backed event emitter that provides consistent API for emitting and listening to events with full TypeScript support.

/**
 * A class that provides a consistent API for emitting and listening to events.
 * Implementation is written in C++ and common for all platforms.
 * Events are emitted synchronously and any returned values are ignored.
 */
class EventEmitter<TEventsMap extends EventsMap = Record<never, never>> {
  /**
   * Creates a new event emitter instance
   */
  constructor();
  
  /**
   * @deprecated Creating from existing object not necessary as of SDK 52
   */
  constructor(object: EventEmitter);
  
  /**
   * Adds a listener for the given event name
   * @param eventName - Name of the event to listen for
   * @param listener - Function to call when event is emitted
   * @returns Subscription object for removing the listener
   */
  addListener<EventName extends keyof TEventsMap>(
    eventName: EventName,
    listener: TEventsMap[EventName]
  ): EventSubscription;
  
  /**
   * Removes a specific listener for the given event name
   * @param eventName - Name of the event
   * @param listener - The exact listener function to remove
   */
  removeListener<EventName extends keyof TEventsMap>(
    eventName: EventName,
    listener: TEventsMap[EventName]
  ): void;
  
  /**
   * Removes all listeners for the given event name
   * @param eventName - Name of the event to clear
   */
  removeAllListeners(eventName: keyof TEventsMap): void;
  
  /**
   * Synchronously calls all listeners attached to the event
   * @param eventName - Name of the event to emit
   * @param args - Arguments to pass to the listeners
   */
  emit<EventName extends keyof TEventsMap>(
    eventName: EventName,
    ...args: Parameters<TEventsMap[EventName]>
  ): void;
  
  /**
   * Returns the number of listeners for the given event
   * @param eventName - Name of the event
   * @returns Number of active listeners
   */
  listenerCount<EventName extends keyof TEventsMap>(eventName: EventName): number;
  
  /**
   * Called automatically when first listener is added
   * Override in subclass for additional setup
   * @param eventName - Name of the event being observed
   */
  startObserving?<EventName extends keyof TEventsMap>(eventName: EventName): void;
  
  /**
   * Called automatically when last listener is removed
   * Override in subclass for cleanup
   * @param eventName - Name of the event no longer observed
   */
  stopObserving?<EventName extends keyof TEventsMap>(eventName: EventName): void;
}

Usage Examples:

import { EventEmitter, type EventSubscription } from "expo-modules-core";

// Define event types for type safety
type MyEvents = {
  userLogin: (userId: string, timestamp: number) => void;
  dataReceived: (data: { id: string; content: string }) => void;
  error: (error: Error) => void;
};

// Create typed event emitter
const emitter = new EventEmitter<MyEvents>();

// Add listeners with full type safety
const loginSubscription = emitter.addListener("userLogin", (userId, timestamp) => {
  console.log(`User ${userId} logged in at ${timestamp}`);
});

const dataSubscription = emitter.addListener("dataReceived", (data) => {
  console.log(`Received data: ${data.content}`);
});

// Emit events with type checking
emitter.emit("userLogin", "user123", Date.now());
emitter.emit("dataReceived", { id: "msg1", content: "Hello World" });

// Check listener count
console.log(emitter.listenerCount("userLogin")); // 1

// Remove specific listeners
loginSubscription.remove();

// Remove all listeners for an event
emitter.removeAllListeners("dataReceived");

EventSubscription Interface

Subscription object returned when adding event listeners, providing a convenient way to remove listeners.

/**
 * A subscription object that allows convenient removal of event listeners
 */
interface EventSubscription {
  /**
   * Removes the event listener associated with this subscription
   * After calling this, the listener will no longer receive events
   */
  remove(): void;
}

Usage Examples:

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

const emitter = new EventEmitter<{ message: (text: string) => void }>();

// Store subscription for later cleanup
const subscription = emitter.addListener("message", (text) => {
  console.log("Message:", text);
});

// Later, remove the listener
subscription.remove();

// Multiple subscriptions can be stored and cleaned up
const subscriptions: EventSubscription[] = [
  emitter.addListener("message", handler1),
  emitter.addListener("message", handler2),
  emitter.addListener("message", handler3),
];

// Clean up all subscriptions
subscriptions.forEach(sub => sub.remove());

Custom Event Emitter Subclasses

Create custom event emitters with automatic lifecycle management by extending EventEmitter.

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

// Define your event types
type NetworkEvents = {
  connected: () => void;
  disconnected: (reason: string) => void;
  dataReceived: (data: ArrayBuffer) => void;
};

class NetworkEventEmitter extends EventEmitter<NetworkEvents> {
  private connection: WebSocket | null = null;
  
  // Override startObserving for setup when first listener added
  startObserving(eventName: keyof NetworkEvents) {
    console.log(`Starting to observe ${eventName}`);
    if (eventName === 'connected' && !this.connection) {
      this.setupConnection();
    }
  }
  
  // Override stopObserving for cleanup when last listener removed
  stopObserving(eventName: keyof NetworkEvents) {
    console.log(`Stopped observing ${eventName}`);
    if (eventName === 'connected' && this.listenerCount('connected') === 0) {
      this.teardownConnection();
    }
  }
  
  private setupConnection() {
    // Connection setup logic
    this.connection = new WebSocket('ws://example.com');
    this.connection.onopen = () => this.emit('connected');
    this.connection.onclose = (event) => this.emit('disconnected', event.reason);
  }
  
  private teardownConnection() {
    // Connection cleanup logic
    this.connection?.close();
    this.connection = null;
  }
}

// Usage
const networkEmitter = new NetworkEventEmitter();

// Adding first listener triggers startObserving
const subscription = networkEmitter.addListener('connected', () => {
  console.log('Network connected!');
});

// Removing last listener triggers stopObserving
subscription.remove();

Types

/**
 * Base type for events map defining event names and their listener signatures
 */
type EventsMap = Record<string, (...args: any[]) => void>;