Services

Introduction

This tutorial teaches how to use services in IoC+. Services are classes that allow actions on (usually) external sources. This could be logging in, posting a message online, or load content for a certain level of a game. IoC+ makes it really easy to switch the services of a certain type. This way we can switch between online and offline content based on whether we are in a development or production context.

This tutorial starts with a fresh new project. If this is the first time using IoC+, we suggest to read the introduction first.

Local Content Service

In this tutorial we’re going to make two implementations of a service that will load content. When setting up a service, even with only one implementation, it is always good practice to set up an interface for it. Create a new script called “IContentService.cs” and add the following code.

using System;

public interface IContentService {

    void LoadContent(Action<string> onDone);

}

The interface demands an implementation of a LoadContent() method that takes an action which will return the content when loaded. Let’s write our first implementation. Create a new script called “LocalContentService.cs” and add the following code.

using System;

public class LocalContentService : IContentService {

    const string CONTENT = "Local content.";

    public void LoadContent(Action<string> onDone) {
        if (onDone != null) {
            onDone(CONTENT);
        }
    }

}

The LocalContentService implements the LoadContent() method by simply calling the onDone action with a static string. Of course, when implementing a real service, we would write code to read and possibly deserialize local files and return its content. But for this example we’ll keep it as simple as this.

Loading Content

To trigger the service to load the content we’ll need a signal and a command. Create a new script called “LoadContentSignal.cs” and add the following code.

using IoCPlus;

public class LoadContentSignal : Signal { }

Just a simple signal that will trigger the following command. Create a new script called “LoadContentCommand.cs” and add the following code.

using IoCPlus;
using UnityEngine;

public class LoadContentCommand : Command {

    [Inject] IContentService contentService;

    protected override void ExecuteOverTime() {
        contentService.LoadContent(OnContentLoaded);
    }

    private void OnContentLoaded(string content) {
        Debug.Log("Content loaded: " + content);
        Release();
    }
}

We inject the content service by its interface type, we will need to bind that injection in the context.

The command overrides the ExecuteOverTime() method, instead of the Execute() method that we’ve been overriding in the past. When overriding the ExecuteOverTime() method we’ll need to call the Release() method manually to let our command know that it is done executing.

In the ExecuteOverTime() method we call the content service LoadContent() method, which will call the OnContentLoaded() when its done loading content. There, the command logs the loaded content in the debugger. Then it released the command’s execution.

Development Context

Let’s write a development context to bind the service injection and bind the command to the signal. Create a new script called “DevelopmentContext.cs” and add the following code.

using IoCPlus;

public class DevelopmentContext : Context {

    protected override void SetBindings() {
        base.SetBindings();

        Bind<IContentService, LocalContentService>();
        Bind<LoadContentSignal>();

        On<LoadContentSignal>().Do<LoadContentCommand>();
    }

}

We bind the LocalContentService as an injection to the IContentService type. This makes sure that all commands and mediators that inject the IContentService will get an instance of the LocalContentService. Then, it binds the LoadContentSignal injection, followed by setting the LoadContentCommand as a response to the LoadContentSignal.

Before we can test our creations we’ll need a root content for the development context. Create a new script called “DevelopmentRoot.cs” and add the following code.

using IoCPlus;

public class DevelopmentRoot : ContextRoot<DevelopmentContext> { }

In a new scene, create a new gameobject and add the DevelopmentRoot as a component. Open up the IoC+ Monitor and hit play. The development context should open up and you can test the code by right clicking the LoadContentSignal and dispatching it!

Online Service

One of the awesome features of IoC is that we can add another implementation of the content service and easily switch between the two. Let’s make an online content service, one that will not output the content of a const string but output the content of the google page instead.

We’ll need a coroutine to use Unity’s WWW class, but coroutines can only be made inside a MonoBehavior, which the service isn’t (and should never be). Therefor we’ll write a small helper class that will allow us to use coroutines outside of MonoBehaviors. Create a new script called “Coroutiner.cs” and add the following code.

using System.Collections;
using UnityEngine;

public class Coroutiner : MonoBehaviour {

    private static Coroutiner instance;

    public static void Start(IEnumerator routine) {
        GetInstance().StartLocalCoroutine(routine);
    }

    public void StartLocalCoroutine(IEnumerator routine) {
        StartCoroutine(routine);
    }

    private static Coroutiner GetInstance() {
        if (instance == null) {
            GameObject go = new GameObject("Coroutiner");
            instance = go.AddComponent<Coroutiner>();
        }
        return instance;
    }

}

Let’s not dive into this class too much, as it is does not have to do anything directly with IoC+. What we need to know is that we can use the static Start() method to start an IEnumerator method in any class to start it as a coroutine.

We can now write our online content service. Create a new script called “OnlineContentService.cs” and add the following code.

using System;
using System.Collections;
using UnityEngine;

public class OnlineContentService : IContentService {

    readonly string url;

    public OnlineContentService(string url) {
        this.url = url;
    }

    public void LoadContent(Action<string> onDone) {
        Coroutiner.Start(DownloadContent(onDone));
    }

    private IEnumerator DownloadContent(Action<string> onDone) {
        WWW www = new WWW(url);

        while (!www.isDone) {
            yield return new WaitForEndOfFrame();
        }

        if (onDone != null) {
            onDone(www.text);
        }
    }

}

In the constructor of the OnlineContentService class we can set the url that the service will use as the location of the content. It implements the IContentService and therefor also has the LoadContent() method. Here it uses the Coroutiner class to start the DownloadContent() method as a coroutine. There, we use Unity’s WWW class to download and return the content that is located at the url.

How cool is that? Two services that basically do the same, but in two totally different ways. We could even add a third implementation that we can use for testing! This could greatly reduce testing to as we wouldn’t have to wait for our servers to respond during development.

Production Context

Let’s add another context, this time a context that can be used for the production version of a game. Create a new script called “ProductionContext.cs” and add the following code.

using IoCPlus;

public class ProductionContext : Context {

    protected override void SetBindings() {
        base.SetBindings();

        Bind<IContentService>(new OnlineContentService("http://google.com"));
        Bind<LoadContentSignal>();

        On<LoadContentSignal>().Do<LoadContentCommand>();
    }

}

The ProductionContext basically does the exact same thing as the DevelopmentContext. The only difference is the content service binding. Here, we bind the IContentService injection to an instance of the OnlineContentService type that we construct ourselves. This way we can configure the url that will be used.

Let’s add a root context for the ProductionContext. Create a new script called “ProductionRoot.cs” and add the following code.

using IoCPlus;

public class ProductionRoot : ContextRoot<ProductionContext> { }

Remove the DevelopmentRoot from the gameobject in the scene and replace it with the ProductRoot. Start the game with the IoC+ Monitor open, and you’ll now see the OnlineContentService being bound as injection to the IContentService type. If we’d dispatch the LoadContentSignal, it would now take a bit longer for the command to complete its execution as it will need to load content from a web source. When it is loaded the debugger should log the html content of the google home page!

Choosing Context

Let’s write a small class that will choose which context to spawn according to the environment in which our game is in. Create a new script called “RootSpawner.cs” and add the following code.

using UnityEngine;

public class RootSpawner : MonoBehaviour {

    private void Awake() {
#if UNITY_EDITOR
        gameObject.AddComponent<DevelopmentRoot>();
#else
        gameObject.AddComponent<ProductionRoot>();
#endif
    }

}

This RootSpawner class will attach either the DevelopmentRoot or the ProductionRoot based on whether it is executed in the UnityEditor or on our target platform. Replace the ProductionRoot of the empty gameobject in our scene with the RootSpawner component.

Isn’t this great? No low level code will ever need to check whether to load content locally or online, it is all handled in the simple, comprehensible configuration of the context, chosen by the RootSpawner component.

The IoC+ Monitor

Go ahead and open up the IoC+ Monitor!

The RootSpawner will spawn the DevelopmentContext as we are currently in the Unity Editor, but we can easily change that via the IoC+ Monitor. Just right-click the DevelopmentContext, select Switch Context and select the ProductionContext. From then on, when we dispatch the LoadContentSignal, we will load the content from the web!

Conclusion

In this tutorial we’ve learned how to make services. Services are classes that allow us to load and/or alter on external sources. They are best injected via an interface type so we can easily switch the implementation when needed. We’ve also seen that commands can override the ExecuteOverTime() method alternatively to the Execute() method, but they will need to call the Release() method manually. We’ve also seen a way to spawn a context based on the environment of the game, effectively using the development or production context based on whether we are in the Unity Editor or not.