> ## Documentation Index
> Fetch the complete documentation index at: https://docs-unity.molca.id/llms.txt
> Use this file to discover all available pages before exploring further.

# EventDispatcher

> String-keyed and generic publish/subscribe events.

**File:** `Packages/com.molca.core/Runtime/Events/EventDispatcher.cs`\
**Subsystem:** registered with `RuntimeManager` like other services

## When to use

Use `EventDispatcher` when you need **decoupled, cross-component communication** without direct references:

* **Cross-system notifications** (e.g. language changed, app initialized, session events) where publisher and subscriber live in different assemblies or scenes.
* **String-keyed events with optional typed payloads** — avoids coupling to concrete event classes.
* Prefer **`UnityEvent`** for Inspector-wired, single-object callbacks. Prefer **C# `event`** for tightly-coupled same-class patterns. Use `EventDispatcher` for everything else.

## TypedEvents

**File:** `Events/TypedEvents.cs` — wraps common event names. Prefer these over magic strings where entries exist (scene load, modals, etc.).

## Using from code

Use a normal `MonoBehaviour` with an injected `EventDispatcher`, subscribe in `OnEnable`, and always unsubscribe in `OnDisable`.

```csharp theme={null}
using UnityEngine;
using Molca;
using Molca.Events;

public class SessionStatusBanner : MonoBehaviour
{
    [SerializeField] private string status;

    [Inject] private EventDispatcher _eventDispatcher;

    private async void Start()
    {
        await RuntimeManager.WaitForInitialization();
        if (_eventDispatcher == null)
            RuntimeManager.InjectDependencies(this);
    }

    private void OnEnable()
    {
        if (_eventDispatcher == null) return;

        // Typed/event-constant style
        _eventDispatcher.RegisterEvent(EventConstants.Application.Initialized, OnAppInitialized);

        // Generic payload style
        _eventDispatcher.RegisterEvent<string>(EventConstants.UI.LanguageChanged, OnLanguageChanged);
    }

    private void OnDisable()
    {
        if (_eventDispatcher == null) return;
        _eventDispatcher.UnregisterEvent(EventConstants.Application.Initialized, OnAppInitialized);
        _eventDispatcher.UnregisterEvent<string>(EventConstants.UI.LanguageChanged, OnLanguageChanged);
    }

    public void MarkReady()
    {
        // Publish event to other systems
        _eventDispatcher?.DispatchEvent(EventConstants.Application.Initialized);
    }

    private void OnAppInitialized()
    {
        status = "Initialized";
    }

    private void OnLanguageChanged(string languageCode)
    {
        status = $"Language: {languageCode}";
    }
}
```

## API Reference

| Method               | Signature                                                       | Description                                            |
| -------------------- | --------------------------------------------------------------- | ------------------------------------------------------ |
| `RegisterEvent`      | `void RegisterEvent(string eventName, Action callback)`         | Subscribe to a parameterless event                     |
| `RegisterEvent<T>`   | `void RegisterEvent<T>(string eventName, Action<T> callback)`   | Subscribe to a typed-payload event                     |
| `UnregisterEvent`    | `void UnregisterEvent(string eventName, Action callback)`       | Unsubscribe parameterless — call from `OnDisable`      |
| `UnregisterEvent<T>` | `void UnregisterEvent<T>(string eventName, Action<T> callback)` | Unsubscribe typed — call from `OnDisable`              |
| `DispatchEvent`      | `void DispatchEvent(string eventName)`                          | Publish a parameterless event                          |
| `DispatchEvent<T>`   | `void DispatchEvent<T>(string eventName, T eventData)`          | Publish a typed-payload event                          |
| `ClearEvent`         | `void ClearEvent(string eventName)`                             | Remove all handlers for a specific event               |
| `ClearAllEvents`     | `void ClearAllEvents()`                                         | Remove all handlers (called automatically on Shutdown) |

## Troubleshooting

* **Event not received:** confirm `RegisterEvent` runs **before** the first `DispatchEvent`. If subscription is in `OnEnable` but dispatch fires during `RuntimeManager` init, the handler may not be registered yet — move subscription to `Start` with `await RuntimeManager.WaitForInitialization()`.
* **Duplicate callbacks firing:** calling `RegisterEvent` twice with the same callback adds it twice via `Delegate.Combine`. Always pair with `UnregisterEvent` in `OnDisable` to prevent stacking across enable/disable cycles.
* **Missing unsubscribe causes errors after scene unload:** destroyed objects' callbacks remain registered and throw `MissingReferenceException`. Always call `UnregisterEvent` in `OnDisable` or `OnDestroy`.
* **Wrong type parameter on `DispatchEvent<T>`:** the type key must match exactly between publisher and subscriber. `DispatchEvent<int>` will not reach `RegisterEvent<float>` handlers even with the same event name.

## Related

* [RuntimeManager](/core/runtime-manager) — `EventDispatcher` is a `RuntimeSubsystem` on the prefab
* [Dependency injection](/core/dependency-injection) — use `[Inject]` to get the dispatcher
* [Localization](/core/localization) — dispatches via `TypedEvents.LanguageChanged`
* [Recipe: Create and dispatch custom events](/recipes/create-custom-events) — step-by-step guide to using EventDispatcher for decoupled communication
