> ## 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.

# Implement dependency injection in custom components

> Use [Inject] attribute to access services in custom MonoBehaviour components.

**Applies to:** Molca Core

## Overview

This recipe shows you how to use the `[Inject]` attribute to access RuntimeManager services (like `EventDispatcher`, `DataManager`, `ReferenceManager`) in your custom `MonoBehaviour` components. Dependency injection eliminates the need for singleton patterns or manual service lookups, making your code cleaner and more testable. You'll learn how to inject services into scene objects and runtime-created objects.

## Prerequisites

* **SDK modules**: Molca Core installed
* **Unity setup**: RuntimeManager configured in your scene
* **Prior knowledge**: [RuntimeManager](/core/runtime-manager), [Dependency injection](/core/dependency-injection)

## Step-by-step

### Step 1: Add \[Inject] attribute to service fields

Mark service fields with the `[Inject]` attribute. The RuntimeManager will automatically populate these fields after initialization.

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

public class MyFeatureController : MonoBehaviour
{
    // These fields will be injected automatically
    [Inject] private EventDispatcher _eventDispatcher;
    [Inject] private ReferenceManager _referenceManager;
    
    // Optional: allow missing service without logging an error
    [Inject(Required = false)] private DataManager _dataManager;
}
```

**Why this works**: The `RuntimeManager` scans all scene `MonoBehaviour` components after initialization and fills fields marked with `[Inject]`. The `Required` parameter controls whether a missing service logs an error (default: `true`).

### Step 2: Wait for RuntimeManager initialization

Always call `await RuntimeManager.WaitForInitialization()` in `Start` before using injected services. Injected fields are `null` in `Awake` and early `Start`.

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

public class MyFeatureController : MonoBehaviour
{
    [Inject] private EventDispatcher _eventDispatcher;

    private async void Start()
    {
        // Wait for RuntimeManager to complete initialization and injection
        await RuntimeManager.WaitForInitialization();
        
        // Now it's safe to use injected services
        if (_eventDispatcher == null)
        {
            Debug.LogError("EventDispatcher injection failed");
            return;
        }
        
        // Use the service
        _eventDispatcher.RegisterEvent("MyEvent", OnMyEvent);
    }

    private void OnMyEvent()
    {
        Debug.Log("Event received");
    }
}
```

**Why this works**: `RuntimeManager.WaitForInitialization()` blocks until `RuntimeManager.IsReady` is `true`, which happens after all subsystems are initialized and scene injection is complete.

### Step 3: Inject dependencies into runtime-created objects

Objects created with `Instantiate` or `AddComponent` don't get automatic injection. Call `RuntimeManager.InjectDependencies()` manually after creating them.

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

public class DynamicObjectSpawner : MonoBehaviour
{
    [SerializeField] private GameObject prefab;

    private async void Start()
    {
        await RuntimeManager.WaitForInitialization();
    }

    public void SpawnObject()
    {
        // Instantiate the prefab
        GameObject instance = Instantiate(prefab);
        
        // Get the component that needs injection
        MyFeatureController controller = instance.GetComponent<MyFeatureController>();
        
        // Manually inject dependencies
        if (controller != null)
        {
            RuntimeManager.InjectDependencies(controller);
        }
    }
}
```

**Why this works**: The automatic scene injection pass only runs once during `RuntimeManager` initialization. Runtime-created objects must call `InjectDependencies()` explicitly to populate their `[Inject]` fields.

### Step 4: Use InjectDependencies pattern for late initialization

If your component might be created before `RuntimeManager` is ready, use the `InjectDependencies` pattern to handle both cases.

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

public class LateInitializedComponent : MonoBehaviour
{
    [Inject] private EventDispatcher _eventDispatcher;

    private async void Start()
    {
        await RuntimeManager.WaitForInitialization();
        
        // If injection didn't happen automatically, do it manually
        if (_eventDispatcher == null)
        {
            RuntimeManager.InjectDependencies(this);
        }
        
        // Now safe to use
        _eventDispatcher?.RegisterEvent("AppReady", OnAppReady);
    }

    private void OnAppReady()
    {
        Debug.Log("Application ready");
    }
}
```

**Why this works**: This pattern handles both scene objects (which get automatic injection) and runtime-created objects (which need manual injection). The null check ensures injection happens exactly once.

## Complete example

Here's a complete example showing dependency injection in a custom training step that uses multiple services:

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

/// <summary>
/// A custom step that uses EventDispatcher and ReferenceManager via dependency injection.
/// </summary>
public class InteractiveTrainingStep : Step
{
    [SerializeField] private string targetObjectId = "TrainingTarget";
    [SerializeField] private string completionEventName = "TargetInteracted";

    // Injected services
    [Inject] private EventDispatcher _eventDispatcher;
    [Inject] private ReferenceManager _referenceManager;

    private GameObject _targetObject;

    private async void Start()
    {
        // Wait for RuntimeManager initialization
        await RuntimeManager.WaitForInitialization();
        
        // Verify injection succeeded
        if (_eventDispatcher == null || _referenceManager == null)
        {
            Debug.LogError($"{nameof(InteractiveTrainingStep)}: Dependency injection failed");
            return;
        }
    }

    protected override void OnStepActivated()
    {
        base.OnStepActivated();
        
        // Use ReferenceManager to find the target object
        _targetObject = _referenceManager?.GetReferencedObject(targetObjectId);
        
        if (_targetObject != null)
        {
            Debug.Log($"Step activated: interact with {_targetObject.name}");
            
            // Register for completion event
            _eventDispatcher?.RegisterEvent(completionEventName, OnTargetInteracted);
        }
        else
        {
            Debug.LogWarning($"Target object '{targetObjectId}' not found");
        }
    }

    protected override void OnStepDeactivated()
    {
        base.OnStepDeactivated();
        
        // Always unregister events to prevent memory leaks
        _eventDispatcher?.UnregisterEvent(completionEventName, OnTargetInteracted);
    }

    private void OnTargetInteracted()
    {
        Debug.Log("Target interacted, completing step");
        Complete();
    }
}
```

This step demonstrates:

* Injecting multiple services (`EventDispatcher`, `ReferenceManager`)
* Waiting for initialization in `Start`
* Using injected services in lifecycle methods
* Proper cleanup in `OnStepDeactivated`

## Troubleshooting

* **Injected fields are null**: Ensure you call `await RuntimeManager.WaitForInitialization()` before accessing injected services. Fields are `null` until the injection pass completes.
* **Runtime-created objects have null fields**: Objects created with `Instantiate` or `AddComponent` don't get automatic injection. Call `RuntimeManager.InjectDependencies(instance)` after creating them.
* **"Service not registered" error**: The service you're trying to inject isn't registered with `RuntimeManager`. Check that the corresponding `RuntimeSubsystem` is attached to the RuntimeManager prefab and enabled.
* **Injection works in Editor but not in build**: Verify that the RuntimeManager prefab is referenced in [Molca Project Settings](/setup/molca-project-settings) and that the prefab is included in the build.
* **Multiple injection calls cause issues**: Calling `InjectDependencies()` multiple times on the same object is safe — it will re-inject fields but won't cause errors.

## Related

* [Dependency injection](/core/dependency-injection) — detailed documentation on the injection system
* [RuntimeManager](/core/runtime-manager) — service container and initialization manager
* [EventDispatcher](/core/event-dispatcher) — commonly injected service for event communication
* [ReferenceManager](/core/reference-system) — commonly injected service for object lookup
