Client not receiving services

  06. Common Problems & Solutions No Comments

Issue

A client component that derives from MonoBehaviour<T…>, does not receive any services via its Init method.

Solution #1: OnAwake

One possible reason for this, is that an Awake method has been defined in the client’s class. If this is is done, it will prevent the Awake method in the base class from being called.

If the error originates from an Awake method, this is likely to be the source of the problem:

NullReferenceException: Object reference not set to an instance of an object
MyComponent.Awake () (at Assets/Scripts/MyComponent.cs:12)

To fix the issue, change the void Awake() definition to protected override void OnAwake().

❌ Wrong

public class MyClient : MonoBehaviour<MyService>
{
    MyService myService;
    protected override void Init(MyService myService) => this.myService = myService;

    void Awake() => myService.DoSomething();
}

✅ Correct

public class MyClient : MonoBehaviour<MyService>
{
    MyService myService;
    protected override void Init(MyService myService) => this.myService = myService;

    protected override void OnAwake() => myService.DoSomething();
}

The OnAwake method is called at the end of the Awake event for the component, after the component has received the objects that it depends on via its Init method.

Solution #2: Initializer

One possible reason for this is that the component requires one or more objects that are not registered as services using the ServiceAttribute or by a Service Tag.

Components that derive from MonoBehaviour<T…> will only autonomously receive all the objects that they depend on, if all the objects are registered services that are accessible to the client.

To fix the issue, generate an Initializer for the client component, and attach it to it. You will then be able to configure all the required objects that are not registered services using the Inspector window.

Solution #3: Script Execution Order

If you are instantiating some services manually in code during initialization of a scene, or when transitioning between scenes, then it could be that the issue is that the clients initialization logic gets executed before your code that creates the services does.

To fix the issue, you can change the script execution order setting for the class that creates the services to be very early, before any clients or their initializers are loaded.

One way to do this is to add the DefaultExecutionOrder attribute to the class:

[DefaultExecutionOrder(-31000)]
public class ServicesLoader : MonoBehaviour
{
    [SerializeField] GameObject servicesPrefab;

    void Awake() => Instantiate(servicesPrefab);
}

Alternatively, you can use the InitOrder attribute that Init(args) ships with. It has identical behaviour to the DefaultExecutionOrder attribute, but its parameters offer some more guidance on how different execution order values relate to those of other types of components.

using Sisus.Init;
using UnityEngine;

[InitOrder(Category.ServiceInitializer, Order.VeryEarly)]
public class ServicesLoader : MonoBehaviour
{
    [SerializeField] GameObject servicesPrefab;

    void Awake() => Instantiate(servicesPrefab);
}

Leave a Reply

Your email address will not be published. Required fields are marked *