The need for native mobile apps
Mobile applications are important these days for companies to strengthen the relation with their customers and employees. More and more people use smartphones and tablets instead of PCs for anything you can think of, like socializing, entertainment, shopping or looking for a new job. And people demand the best apps to do this. They prefer native apps (or at least the apps should feel that way), because this gives the best user experience on their mobile devices. This makes it very important for companies to do their mobile applications right.
The challenge from a development point of view is: how to build the best apps for the users, while still be able to address as much target mobile OS platforms as possible. One way to do this is to use one of the many multiplatform frameworks available to build mobile apps from one code base. To accomplish this, often Javascript/HTML5 or lightweight programming languages like Lua are used. Furthermore, these frameworks give access to common functionality like the camera, accelerometer, geolocation, compass, network and storage in a generic way. However, the disadvantage of this approach may be twofold. It is not always possible to gain the best user experience on every platform and also the development experience remains behind by the available native development environments.
Platform-independent architecture
As an alternative, multiple OS platforms can be targeted from one code base using a flexible platform-independent architecture for mobile clients. In this architecture all presentation and business logic is realized in portable platform-independent components, while the UI views and the platform-specific services are realized in platform-specific components (see figure 1). Dependency injection is used to dynamically bind the platform-specific components to the portable components. The idea is to implement as much application logic in a portable way as possible, and at the same time realize the best user experience with native views and services for each platform.
Figure 1: layered architecture
To realize the portable components in this layered architecture, .NET is a suitable technology that offers platform-independent development and runtime environments. C# and the CLI is an open standard (see
http://msdn.microsoft.com/en-us/netframework/aa569283) that is available for iOS (
MonoTouch), Android (
Mono for Android) and
Windows.The platform-specific components bridge the gap to native functionality using the invocation of native Objective-C libraries on iOS and Dalvik runtime libraries on Android.
The Model-View-Presenter pattern
A suitable multiplatform design pattern that can be used for all mobile platforms is the Model-View-Presenter pattern. This pattern is heavily used for mobile apps on multiple platforms. It offers good separation of concerns for user interface, application orchestration, presentation and business logic, and platform services. Figure 2 shows how the Model-View-Presenter pattern can be applied in a multiplatform mobile architecture. The blue classes are the platform-specific classes, while the yellow classes are platform-independent and can be used on multiple platforms unchanged.
Figure 2: Model-View-Presenter pattern
A description and the responsibilities of the classes shown in figure 2:
ApplicationContainer
Platform-specific class that is responsible for hosting the application for a specific platform. It instantiates the Application class and arranges the bindings for the platform-specific views and services needed. See the next paragraph for more information about dependency injection binding.
Application
Platform-independent class that implements the application. This class is responsible for the application functionality at a workflow level and for the orchestration between views. This class aggregates the Model from the Model-View-Presenter pattern, which is represented by the BusinessEntity class.
Presenter
Platform-independent class that implements the presentation logic for a view that is accessed via a specific IView interface. The model used by the presentation logic, represented by the BusinessEntity class is directly accessed via an association. User interaction that results in orchestration between views is reported to the Application class via a callback or an event.
ServiceFactory<IView>
Platform-independent association class to dynamically bind the IView interface with the platform-specific View implementation.
IView
Platform-independent interface which is specifically designed per view to interact between a platform-specific View class and a platform-independent Presenter class. The View class interacts with the Model directly for simple data-binding and updates. (This is the Supervising Controller variant of the Model-View-Presenter pattern as opposite to the Passive View variant, wherein all interaction with the Model is done only by the presenter.) This offers more freedom to vary the UI implementation across multiple platforms.
View
Platform-specific class that is responsible to realize a screen, multiple screens or part of a screen. User interaction that results in presentation logic is reported to the Presenter class via a callback or an event.
BusinessEntity
Platform-independent class that actually represents the Model in the Model-View-Presenter pattern. The model consists of multiple business entity classes that contain the business logic of the mobile application. Implementing the business logic in separate business entity classes gives an additional advantage. It offers the flexibility to move the business logic to a mobile web client or business service implementation on the server side when desired. Platform-specific services, devices and data are accessed via specific IService interfaces.
ServiceFactory<IService>
Platform-independent association class to dynamically bind the
IService interface with the platform-specific
Service implementation.
IService
Platform-independent interface which is specifically designed to interact with a specific platform-specific Service that is needed in the mobile application. It is not necessary to expose complete platform services, but only implement the APIs actually needed for the mobile application in an agile way.
Service
Platform-specific service that realizes the integration with specific platform or MEAP (Mobile Enterprise Application Platform) services, web services, data access and storage, devices and more. These service realizations will be kept small, because only the APIs are exposed which are actually needed for the mobile application involved. Note that a lot of platform services are exposed in an uniform way for multiple platforms, which makes it possible to also share service components across those platforms.
Dependency Injection binding
Dependency Injection is a design pattern that offers the possibility to dynamically bind the consumer of an interface or component to the implementation. Many frameworks are available for dependency injection that can be used in this architecture. However, because not much is needed to implement the
ServiceFactory<> template class for this mobile architecture, a simple .NET example implementation is listed here.
using System;
using System.Collections.Generic;
namespace Mobile.Common.Framework
{
public sealed class ServiceFactory<TServiceInterface>
where TServiceInterface : class
{
// Nested types.
/// <summary>
/// This interface enables the access of the binding independent
/// of type.
/// </summary>
private interface IBindingInterface
{
/// <summary>
/// Create a new instance of the type bound to the interface.
/// </summary>
/// <returns>The interface for this service.</returns>
TServiceInterface Create();
}
/// <summary>
/// The template used to handle the current binding.
/// </summary>
private class Binding<TBindingType> : IBindingInterface
where TBindingType : class, TServiceInterface, new()
{
/// <summary>
/// Create a new instance of the type bound to the interface.
/// </summary>
/// <returns>The interface for this service.</returns>
public TServiceInterface Create()
{
return new TBindingType();
}
}
// Private members.
static private IBindingInterface _binding = null;
static private TServiceInterface _instance = null;
private ServiceFactory(){}
/// <summary>
/// Bind type to this interface.
/// </summary>
/// <typeparam name="TBindingType">Type to be bound.</typeparam>
static public void Bind<TBindingType>()
where TBindingType : class, TServiceInterface, new()
{
_binding = new Binding<TBindingType>();
}
/// <summary>
/// Bind instance to this interface.
/// </summary>
/// <param name="instance">Instance to be bound.</param>
static public void Bind(TServiceInterface instance)
{
_instance = instance;
}
/// <summary>
/// Retrieve one and only instance.
/// </summary>
/// <returns>The instance.</returns>
static public TServiceInterface GetInstance()
{
if (_instance == null)
{
if (_binding != null)
{
// Create bound type.
_instance = _binding.Create();
}
}
return _instance;
}
}
}
Code listing 1: The ServiceFactory<> template class
ServiceFactory<IMyInterface>.Bind<MyImplementation>();
Code listing 2: Binding the interface in platform-specific code
IMyInterface myService = ServiceFactory<IMyInterface>.GetInstance();
Code listing 3: Using the interface in platform-independent code