Login Example with MVP Architecture in Unity

Lalo Berro
10 min readDec 15, 2022

--

Hi everyone!

In this guide we will be applying what was seen in the MVP post: https://medium.com/@laurencioberro/mvp-arquitecture-in-unity-6fcdb993976b

I leave the Github Repository with all the Sources:
https://github.com/LaloBerro/MVP-LoginExample

Inside the Repository there are two folders, one with all the Exercise resolved and another with the basic elements to be able to follow this guide.

What we are going to do

In this exercise we are going to apply MVP in a login, for this we are going to follow the following steps:

  1. Create a Unity Project and the Login scene.
  2. Create the folder structure that we are going to use.
  3. Create the scripts corresponding to the Domain.
  4. Create the Mock Implementations of the Gateways layer.
  5. Create the scripts corresponding to the Presenter.
  6. Create the scripts corresponding to the View.
  7. Create the Installers.
  8. Set up the scene and test it.
  9. Create the corresponding assemblies.

Diagram

Creating the Unity Project

For this you can download the Repository that I left above or start a project from scratch in any version of unity.

Inside the Repository there is a scene called Login Source, inside there is a Canvas with two inputFields, one for the Username and another for the Password, and a button to login.

Creating the Folders structure

Although in a real case it is not useful to create the folders before coding, it seems to me the right thing for this guide, so they get an idea of where everything goes.

The structure would look like this:

Creating the Domain

Entities

First we are going to create the entities that we are going to need, in this case we will create a class called UserLoginData:

Note that we have a string for the Username and another for the Password. Also that they are declared as readonly since throughout the execution the values of the variables will only be assigned by the constructor and we will access them through a property.

This Entity class is also known as DTO (Data Transfer Object), this means that it is in charge of containing data that is going to be transferred, either within the model or presenter and the main characteristic of a DTO is that it is created for its use and is destroyed when it is finished, so each time we use them we create a new one.

For more information about Entities and DTO, I leave you this post that expatiates enough on the subject:
https://www.linkedin.com/pulse/difference-between-entity-dto-what-use-instead-omar-ismail/?trk=pulse-article_more-articles_related-content-card

Once UserLoginData.cs is created, it would look like this in the folders:

UseCase

Now we will go on to create the UsesCases of our login where the core of our program will be centered.

First we create an interface called ILoginUseCase, it contains a Login(UserLoginData) method, later this interface will be the one that will communicate with the Presenter so that it can consume it:

Then we create an implementation of ILoginUseCase called LoginUseCase, it contains a reference to an IService<UserLoginData>, below I explain what this interface is about:

Finally we create a templated or generic interface called IService<DataType>, being DataType the generic type, this interface will be implemented by the Gateways classes to contain all calls to the server:

Once we have this ready, we will have the folders like this:

Gateways

As I explained before, the Gateways are implementations of IService<UserLoginData> that connect to the server to be able to login. In our case we do not have any server to login to, so we will implement Mocks that meet the characteristics we need.

We create a class called MockLoginService that inherits from IService<UserLoginData>, and we implement our substitute server, then this Mock receives the data from LoginUseCase and checks if the username and password match the ones we “Hardcoded” in the mock, if so, print it “Login Success” but “The password or the username is wrong”:

Once we have this ready, we would have the folders like this:

Creando la capa de Presenter

Now we will create the scripts of the Presenter Layer, as a clue we know that the ILoginUseCase needs a UserLoginData so our presenter has to take care of building it and passing it to the ILoginUseCase.

We create a class called LoginPresenter that has references to ILoginUseCase to be able to pass the UserLoginData and a reference to ILoginView to which we will subscribe through an Action so that it notifies us when the user clicks the button, it will also provide us with the Username and Password entered by the user:

Now we need to create the interface that will later implement the View, it will be called ILoginView:

Once all this is created, the folders would be:

Creating the View

The View will be in charge of obtaining all the data related to the user through UI elements.

We create a class called LoginView that implements Monobehaviour and ILoginView, it will have references to two InputFields, one to insert the username and the other for the password, it also has a reference to a Button that will invoke the Action OnClickOnLogin every time it is clicked:

Creating this class the folders are:

Creating the Installers

So far we have only created classes and interfaces but we have not been able to test anything, so now we will create the Installers so that our program can be initialized and run. In addition, these installers will help us to apply manual Dependency injection.

An Installer has the task of initializing a class, but what happens when a class depends on another to be initialized, for example the case of LoginUseCase if or if it needs a reference to an ILoginPresenter, to solve this we are going to create an Installers system that allows initialize and pass references to other installers.

We start by creating an abstract class called MonoInstaller, it inherits from Monobehaviour to be able to use it in the editor, and it contains an Install() method that will be in charge of initializing the class we want, this method will be externally by a class that we will see later:

Then we create an abstract class called MonoInstallerGeneric, this is a generic class and TypeToInstall will be the type of which we want others to use (if you understood this very well, don’t worry about getting it with the examples), this class contains a property that will be The class we initialize inherits from MonoInstaller because it overrides the Install() method, and it has an abstract method that each child should implement depending on the type of data it is:

Then we create the class called MonoInstaller that will be in charge of Installing the MonoInstallers that we want in order, it also inherits from MonoInstaller:

Finally we need to somehow initialize these installers, because we are going to create a class called MonoInstallerInitializer that initializes a MonoInstaller on awake:

Now that we have the base to be able to initialize the classes, let’s get to that.

First we are going to create all the Installers:

View Installers

We are going to create a MonoInstallerGeneric for LoginView, so we create a class called LoginViewInstaller that inherits from MonoInstallerGeneric<ILoginView>, but because the generic data is ILoginView and not LoginView, since what we want is that the classes that need this reference, like LoginPresenter, they do not need the concretion (LoginView) but the abstraction (ILoginView), in this way we can implement all the ILoginView that we want and we only have to change the reference to the MonoInstaller:

Presenter Installers

We create the MonoInstallerGeneric for LoginPresenter, and notice that it has reference to other MonoInstallerGeneric, this is to be able to access its already initialized class to be able to initialize LoginPresenter:

Model Installers

We create the MonoInstallerGeneric for LoginUse, note that it inherits from MonoInstallerGeneric<ILoginUseCase> and has a reference to the Service installer:

Now we create the MonoInstallerGeneric for the MockLoginService, and note that it inherits from MonoInstallerGeneric<IService<UserLoginData>> :

With all this created the folders would be:

Arming everything in unity

Now we need to put everything together in unity to make it work

  1. We create an Empty Gameobject and call it Code.

2. Inside it we create an Empty Gameobject we call it Login.

3. We create an Empty Gameobject, call it Login-MonoInstallerInitializer, and add the MonoInstallerInitializer component to it.

4. We create another Empty Gameobject we call it Login-Instalator and assign it the component MonoInstalator.

5. Within Login-MonoInstaller we are creating a Gameobject and assigning each MonoInstaller

View
LoginViewInstaller
MockLoginServiceInstaller
LoginUseCaseInstaller
LoginPresenterInstaller

6. Now we drag the necessary references.

For the view, create an Empty Gameobject called LoginView and assign the LoginView component to it, as well as drag the dependencies of the scene to it.

LoginView

Now we drag the necessary dependencies for each Installer:

LoginViewInstaller
LoginUseCaseInstaller
LoginPresenterInstaller

7. Now we drag the MonoInstallers to the Login-MonoInstaller

MonoInstalator

Note that the order in which we put the MonoInstallers is very important, since they are installed in order, that means first you have to install the LoginView and LoginUseCase to be able to install LoginPresenter, if this order is not fulfilled then it will throw a reference error null.

8. Finally we drag the Login-MonoInstaller to the Login-MonoInstallerInitializer.

MonoInstallerInitializer

Ready! Now our program is ready to go!

Final Touches

Although our application is already working correctly, we need to do one more thing so that our scripts are completely organized, and for this we are going to create AssemblyDefinitions.

Assembly Definition

An Assembly Definition is a file that groups scripts, by default if we do not create any Assembly for our project our scripts will be grouped in a default assembly created by unity called Assembly-CSharp.asmdef, the problem of not working with Assemblies is that we do that all the scripts can be known without any problem. And what we want to achieve is that the scripts are known when we want. On the other hand, having Assemblies will make us respect The Dependency Rule.

To create one you have to right click on the project folder where you want to create it and then click on Create>AssemblyDefinition:

The nomenclature used for Assemblies is:

PlaceWhereIsRunned.NameOfTheFeatureOPackage.LayerWhichCorresponds

PlaceWhereItExecutes: This can be Runtime or Editor.
NameOfTheFeatureOPackage: This is the name of whatever we are working on, in our case it will be Login.
LayerToWhichCorresponds: refers to the layer that corresponds to either View, Model or Presenter.

In our case, an example would be:

Runtime.Login.Model

So now we are going to create an Assembly for each layer within the Login scripts. It is normal that the process of creating the Unity Assemblies throws a lot of reference errors, these errors will disappear when we finish creating all the Assemblies. First we are going to create them and then we are going to assign the references

View Assembly

We create an assembly in the View folder and call it:

Runtime.Login.Views

Presenter Assembly

Runtime.Login.Presenters

Model Assembly

Runtime.Login.Models

Installers Assembly

Runtime.Login.Installers

Now we will assign the references, they are used for one Assembly to know the scripts of another and they cannot be cyclical, that is, only one Assembly can know the other, not both.

View Assembly

Inside Assembly Definition References we drag the Assembly of the TextMeshPro and that of the Presenters

Presenter Assembly

Model Assembly

You don’t need any reference.

Installers Assembly

With this last tweak, our code is completely organized and safe so that no external code can touch it without first having a reference to its Assembly.

Conclusion

As we could see, implementing an architecture is very complicated for us and it brings us great benefits.

I hope this post was helpful to you! and if you have any questions you can comment or send me a mail: LaurencioBerro@gmail.com.

Cheers!

--

--

Lalo Berro

Im Lalo a passionate videogame programmer that loves share quality and advanced content.