Reflex
Minimal dependency injection framework for Unity
Install / Use
/learn @gustavopsantos/ReflexREADME
Blazing fast, minimal but complete dependency injection library for <a href="https://unity.com/">Unity</a>
Reflex is an Dependency Injection framework for Unity. Making your classes independent of its dependencies, granting better separation of concerns. It achieves that by decoupling the usage of an object from its creation. This helps you to follow SOLID’s dependency inversion and single responsibility principles. Making your project more readable, testable and scalable.
</div><details> <summary>📌 Table Of Contents</summary>
- Overview
- Installation
- Getting Started
- Execution Order
- Injection Strategy
- Container Hierarrchy
- Scopes
- Bindings
- Resolving
- Selective Resolution Alternative
- Callbacks
- Attributes
- Manual Injection
- Extensions
- Debugger
- Settings
- Performance
- Scripting Restrictions
- Support
- License
👀 Overview
- Fast: up to 414% faster than VContainer, up to 800% faster than Zenject.
- GC Friendly: up to 28% less allocations than VContainer, up to 921% less allocations than Zenject.
- AOT Support: Basically there's no runtime Emit, so it works fine on IL2CPP builds. <sup>[*]</sup>
- Contract Table: Allows usages of APIs like
container.All<IDisposable> - Immutable Container: Performant thread safety free of lock plus predictable behavior.
- Source Generated: Implements modern Roslyn source generator to minimize reflection usage and speed up injection.
Compatible with the following platforms:
- iOS
- Android
- Windows/Mac/Linux
- PS4/PS5
- Xbox One/S/X and Xbox Series X/S
- WebGL
💾 Installation
You can install Reflex using any of the following methods:
Unity Package Manager
https://github.com/gustavopsantos/reflex.git?path=/Assets/Reflex/#14.3.0
- In Unity, open Window → Package Manager.
- Press the + button, choose "Add package from git URL..."
- Enter url above and press Add.
Open Unity Package Manager
openupm install com.gustavopsantos.reflex
Unity Package
- Download the .unitypackage from releases page.
- Import Reflex.X.X.X.unitypackage
🚀 Getting Started
- Install Reflex
- Create
RootInstaller.cswith
using Reflex.Core;
using UnityEngine;
public class RootInstaller : MonoBehaviour, IInstaller
{
public void InstallBindings(ContainerBuilder builder)
{
builder.RegisterValue("Hello"); // Note that values are always registered as singletons
}
}
- In unity project window
- Right click over any folder, Create → Reflex → RootScope. Since RootScope is strongly referenced by ReflexSettings, you can create it anywhere, it does not need to be inside
Resourcesfolder. - Select
RootScopeyou just created - Add
RootInstaller.csas a component - Create directory
Assets/Resources - Right click over
Resourcesfolder, Create → Reflex → Settings. ReflexSettings should always be created directly insideResourcesfolder, without any subfolder. - Select
ReflexSettingsScriptableObject and add theRootScopeprefab to the RootScopes list - Create new scene
Greet - Add
GreettoBuild Settings→Scenes In Build - Create
Greeter.cswith
using UnityEngine;
using System.Collections.Generic;
using Reflex.Attributes;
public class Greeter : MonoBehaviour
{
[Inject] private readonly IEnumerable<string> _strings;
private void Start()
{
Debug.Log(string.Join(" ", _strings));
}
}
- Add
Greeter.csto any gameobject inGreetscene - Inside Greet scene, create a scene scope, Right Click on Hierarchy > Reflex > SceneScope.
- Create
GreetInstaller.cswith
using Reflex.Core;
using UnityEngine;
public class GreetInstaller : MonoBehaviour, IInstaller
{
public void InstallBindings(ContainerBuilder builder)
{
builder.RegisterValue("World"); // Note that values are always registered as singletons
}
}
- Add
GreetInstaller.csto theSceneScopeyou created on step 14 - Create new scene
Boot - Add
BoottoBuild Settings→Scenes In Build - Create
Loader.cswith
using Reflex.Core;
using UnityEngine;
public class Loader : MonoBehaviour
{
private void Start()
{
void InstallExtra(UnityEngine.SceneManagement.Scene scene, ContainerBuilder builder)
{
builder.RegisterValue("of Developers");
}
// This way you can access ContainerBuilder of the scene that is currently building
ContainerScope.OnSceneContainerBuilding += InstallExtra;
// If you are loading scenes without addressables
UnityEngine.SceneManagement.SceneManager.LoadSceneAsync("Greet").completed += operation =>
{
ContainerScope.OnSceneContainerBuilding -= InstallExtra;
};
// If you are loading scenes with addressables
UnityEngine.AddressableAssets.Addressables.LoadSceneAsync("Greet").Completed += operation =>
{
ContainerScope.OnSceneContainerBuilding -= InstallExtra;
};
}
}
- Add
Loader.csto any gameobject atBootscene - Thats it, hit play from
Bootscene - When Greet scene is loaded, there should be 3 instances implementing string contract
- So when Greeter.Start is called, you should see the following log in the unity console:
Hello World of Developers
🎬 Execution Order
<p align="center"> <img src="graphics/execution-order.png" /> </p>🎯 Injection Strategy
As of version 8.0.0 Reflex has stopped automatically managing dependency injection for any scene.
If you plan on using dependency injection in one of your scenes, add a game object somewhere in the hierarchy with a ContainerScope component attached. You can still manage shared dependencies using Root Container or utilize this Scene Containers for limited access. This component must be present at scene load time.
This allows users to consume injected dependencies on callbacks such as Awake and OnEnable while giving more granular control over which scene should be injected or not.
🌱 Container Hierarchy
Default Behaviour
Reflex's default strategy for creating containers involves initially generating a root container. For each newly loaded scene, an additional container is created, which always inherits from the root container. This container hierarchy mirrors the flat hierarchy of Unity scenes. You can see how the structure looks like below:
graph
RootContainer --> BootScene
RootContainer --> LobbyScene
RootContainer --> GameScene
RootContainer --> GameModeTwoScene
Override scene container parent
To do this or whatever else you want with scene ContainerBuilder you can access it with SceneScope.OnSceneContainerBuilding like we show in Loader.cs in "Getting Started" section.
// here we take boot scene container just for an example, you can use any container you need
var bootSceneContainer = gameObject.scene.GetSceneContainer();
void OverrideParent(Scene scene, ContainerBuilder builder) => builder.SetParent(bootSceneContainer);
ContainerScope.OnSceneContainerBuilding += OverrideParent;
// If you are loading scenes without addressables
SceneManager.LoadSceneAsync("Lobby", LoadSceneMode.Additive).completed += operation =>
{
ContainerScope.OnSceneContainerBuilding -= OverrideParent;
};
// If you are loading scenes with addressables
Addressables.LoadSceneAsync("Lobby", LoadSceneMode.Additive).Completed += operation =>
{
ContainerScope.OnSceneContainerBuilding -= OverrideParent;
};
By utilizing this API, you can create hierarchical structures such as the one shown below:
graph
RootContainer-->BootScene
BootScene-->LobbyScene
- Please note that it is not possible to override the parent container for the initial scene loaded by Unity.
- Exercise caution when managing the scene lifecycle with this type of hierarchy. For example, unloading a parent scene before its child scenes can lead to unexpected behavior, as the parent container will be disposed while the child scenes are still active. As a general rule, always unload the scene hierarchy from the bottom up, starting with the child scenes and progressing to the pa
