One of the top mobile development trends today is gamification - the application of game-design elements and game principles in non-game contexts. Gamification implies animated UI-components, and as a result, more fluid navigation between different views (single view application tendency), at the same high level of usability and with well-balanced user flow. The most challenging part of implementing such applications in the MVC framework is to build a controller, managing a complicated view (in terms of architecture and animation), where continuous user interaction is expected.
In this article, I want to tell you how to extend the classic MVC architecture, using a finite-state machine template (FSM) that is widely accepted in game development.
Let’s take for example a classic to-do list as a single view application with rich animation and well-balanced user flow. We have the following UI components: a task list, a top toolbar with a search box and Settings button, and a sidebar with folders. When clicking a task, it is unfolded to the full screen and Task details group box controls are displayed. The controls are: task type selector, task title, text note, geolocation, notification settings, etc. Changing task type (e.g. regular, check list, recurring) is available any time and seamlessly relocates controls on the screen, adding ones and deleting the others. Data entry process is random, bit it’s important that control transfer is seamless and natural.
In the MVC architecture, view contains all the components, model contains data, and controller is a single control class and a delegate of all view components. That’s why controller contains the code for implementing complicated user flow, being bulk, with a lot of helper classes.
Let’s have a look at the following MVC implementation:
Obviously, any complicated flow can be divided in less complicated stages or states. For multi-page applications, where the flow is divided between multiple controllers, the problem is not so actual. For single-page applications, a state machine needs to be clearly defined, so every current state has a full access to view and model, and gets all notifications from components located on the view.
In fact, we are trying to implement the MVC, but with a variable controller. This enables to divide the code between different states, each managing a single stage of the flow, regardless of its complexity.
The advantages of the approach are obvious:
The size of each class-state depends on the complexity of the state, and not on the general number of states.
The logic of each state is stored in the particular class.
A state can easily inherit from another one, as well as have basic methods and helpers that are easily accessible from deriving classes.
Traditional FSM methods– enterState, exitState – enable to track state transition and implement proper animation for each transition.
FSM architecture enables to logically fix state transition, which helps avoid redundant code validation.
Single model and single view provide equal rights and full control to each state, so the entire set of states act as a unit.
We can additionally define event subscription for each state, so a state supports only the events it is supposed to.
The described architecture is perfect not only for implementing upper level of an application, but also for implementing view components of any complexity: such state machines can be easily embedded one into another, which ensures the architecture integrity of the application.
It’s obvious that a component like custom button or text box has simple states. In this case, using simple enumeration as a state machine is enough. But if each state has pretty complicated logic, implementing FSM might be a good option. Later you will enjoy the architecture transparency and ability to scale component complexity, while saving the general code structure.
Big teams will especially appreciate it, as you don’t have to spend much time to make out the structure of the code that is not yours.
Since controller turns into a set of states, let’s define basic state class – BaseState.
To enable each state access model and view, wrap them into StateContext class that is delivered to basic state constructor BaseState.
In this case, view gets special object, that implements previous interface – ViewDelegate.
When event occurs on view, ViewDelegate object gets it and delivers further to the current FSM state, following StateViewDelegate interface. In fact, ViewDelegate object transforms view-event into the state-event, which loosens the link between UI components on view and the state itself.
Eventually, FinalStateMachine class creates all the states, StateContext, StateViewListener.
That’s how MVC+FSM architecture may look like:
As we can see above, now “controllers” (states) use StateContext class to access view and model.
The chain UI-event -> Controller gets just one additional class: UI-event -> ViewListener -> current State. Besides, FinalStateMachine and StateBase classes (required for FSM template) were added.
We can also add transition events enumeration, that lists FSM-level events enabling state transition. We can as well describe acceptable state transition, using Transition class that stores the initial state, the transition event, enabling state transition, and the target state. The set of such Transition objects ensures that FSM executes state transition only when it is permitted.
In conclusion, I want to add, that besides code structure general enhancement, we have created a structure that expands, as the user flow gets more complicated, saving inner transparency and relative simplicity of the particular classes-states.