1. Problem
1.1. Context
You wish to create an interactive application with a flexible human–computer interface that targets as many Symbian OS devices as possible across more than one GUI variant.
1.2. Summary
You wish to be able to easily port your application between the different GUI variants based on Symbian OS.
You want to be able to easily maintain and extend your application over time.
You
want to be able to test as much of your application as possible
independently of the GUI variant to reduce your testing costs.
1.3. Description
Symbian OS supports
multiple GUI variants, which are supplied by UI vendors as a layer on
top of Symbian OS. By supporting multiple GUI variants, Symbian OS
enables device creators to differentiate products based on form factor,
UI 'look and feel', and interaction style. For application developers,
the most important GUI variants are S60 and UIQ. The third GUI variant,
MOAP, is widely used on Symbian OS phones in Japan, but is not open to
native third-party applications so we don't focus on it in this pattern.
Historically, S60
has been associated with classic smartphone designs based on small but
high-resolution displays and key-driven operation, while UIQ has
emphasized larger displays with pen-driven operation and flippable
orientation, evolving from its early support for tablet-style devices to
supporting the signature Sony Ericsson 'flip' phones. More recently S60
and UIQ have shown signs of convergence, as UIQ3 has introduced support
for purely key-driven interaction and S60 has indicated future
intentions to support pen-based interaction styles.
Their 'look and
feel', however, remain distinctively different, and there are
significant differences in implementation detail. Each defines its own
hierarchies of concrete controls (dialogs, windows, and so on), and
while both share a task-based approach, there are subtle differences in
their implementation of the task and application models.
Although the different GUI
variants all derive from common Symbian OS frameworks, they encapsulate
different vendor choices and assumptions about device form factors and
interaction styles. This leads to non-trivial differences between the
variants which means developers have to customize their applications for
each GUI variant they wish to support. Any application that doesn't
control its UI dependencies is more difficult to port between multiple,
independently evolving target GUIs so we need to avoid a monolithic
approach to creating applications.
On any operating system or
platform, an interactive application with more than a trivial
implementation typically has the following responsibilities, regardless
of how they are assigned within the design:
code that manages and manipulates the internal logical state of the application
code that displays a representation of the current application state
code that reacts to events from the end user to manipulate both of the above.
1.4. Example
Consider a contacts
application which at its simplest allows the end user to store and later
retrieve the contact details of the people that they know. Such an
application needs to provide the following functionality:
objects that are
responsible for storing the contact information in the file system so
that it's persisted across reboots of the device but is in an
easy-to-access form
objects
that display the contact details to the end user so that they can
interact with the data to add, update, search or delete contacts
objects
that listen for events generated by the end user such as the text they
wish to search contacts for or the add contact menu item that has been
selected.
In addition to these
core requirements the application needs to work on S60 where the end
user generally uses a joystick-style button to navigate between contacts
and UIQ where the user often taps the screen to select different
contacts.
2. Solution
This pattern is based on
the classic object-oriented abstraction of a graphically based,
data-centric, interactive user application. The classic version of this
pattern, as described in [Buschmann et al.,
1996], provides a ready-made template which is applicable to any
GUI-based application on any platform. The classic pattern recognizes
the three core elements of most interactive applications and recommends
that each is designed as independent modules so that dependencies
between the modules are minimized and the responsibilities are well
encapsulated.
These modules are defined as follows:
The model
owns and manages the data that represents the internal state of the
application and implements all application logic that directly changes
that data, and hence the application state.
The view
provides a view for the end user onto the application state and ensures
that it is always up to date with any changes to that state in the
model.
The controller
provides the interaction logic and interfaces that enable the end user
to interact with the application data, and hence manipulate the
application state.
2.1. Structure
The structure of the classic version of the pattern is shown in Figure 1.
All GUI-based interactive
applications on Symbian OS are implemented using a framework which is
based on the classic version of this pattern, providing a similar level
of abstraction even though it is different in some details:
Symbian OS enforces clear separation between UI-dependent and UI-independent code.
Symbian
OS has historically emphasized the document-centric nature of
applications although this has become less important and both S60 and
UIQ minimize this aspect of the pattern.
Symbian
OS is based on a design in which application classes are derived from
interfaces provided by the application frameworks to provide concrete
implementations of framework-defined behaviors.
As with any
framework-based design, the application frameworks are intended to move
complexity into system code. Thus the framework integrates individual
applications and provides support for:
the application lifecycle of installation, launch, moving to and from the background, and shutdown
handling resources as well as associating documents with individual applications
different
display and graphics models including graphics contexts, concrete
display classes, standard dialogs and other screen elements provided by
the GUI variant
a window hierarchy and system events.
This gives rise on Symbian OS to the basic structure shown in Figure 2.
Every Symbian OS
application must implement the interfaces defined by the application
frameworks. However, the application frameworks can be divided up into
at least two layers: a generic layer provided by Symbian OS with a layer
on top that provides the GUI variant's specializations. Individual
applications then provide concrete implementations of the classes
provided by the GUI variant and hence implement this basic pattern.
Originally, the Symbian OS use
of this pattern assumed that applications are document-centric; an
application is considered to be a document instance, either in a 'live'
running state with an associated process or persisted as serialized data
in a store somewhere in the system. However, subsequent usage has more
typically been to make applications task-centric rather than
document-centric so that the end user experience is typically organized
around user tasks rather than applications and documents. End users are
assumed to be more interested in tasks than in the applications that
perform them and tasks are allowed to follow their natural course, which
is often to flow across multiple applications; for example, starting
with looking up a name in a Contacts application and moving seamlessly
on to calling a number from the Phone application.
In practice, as in Figure 2,
the document class is in many cases simply stubbed out. Instead, the
AppUi directly owns a UI-independent application engine which
encapsulates the application state and logic and is equivalent to the
model in the classic version of this pattern.
Because all interaction
between the model, the view and the controller is through
framework-defined interfaces, there is a high degree of decoupling
inherent in this structure that makes it relatively easy to swap out one
or more classes for an alternative implementation.
Defining at least one view is
essential for an application to display itself. While the classic
Model–View–Controller pattern defines an explicit View class, the
Symbian OS application frameworks do not; instead, a generic control
class CCoeControl is defined, and in early releases of Symbian OS, application views were typically directly derived controls; in special cases
they could be omitted altogether and replaced with a single concrete
control or container. From Symbian OS v9.1 onwards, all standard
application views derive from a suitable framework class supplied by the
GUI variant. In UIQ3 in particular, the view classes provide important
additional functionality, including support for view switching and view
sharing between applications as well as integration with the new generic
command structure.
This structure allows
the UI dependencies of the application to largely be confined to these
views unlike the other two components, the controller and the model,
which should have minimal dependences on the UI and hence are easily
portable between different GUI variants. Views themselves are not
intended to be portable but they are, to a high degree, decoupled from
the model and hence the controller can be replaced for each version of
your application specific to a GUI variant.
2.2. Dynamics
Figure 3 shows how the application classes interact.
Note that in S60 views are sized and switched by the AppUi
whilst in UIQ this is done within the application frameworks. The view
switching in particular highlights an important difference between the
latest versions of UIQ and S60:
In UIQ3, switching to external views,
views that belong to a different application, is explicitly supported
by the framework, encoded in the view persistence and'backing out'
behavior rules, and enshrined in application structure by the
publication of external interface information in header files; the
behavior of built-in UIQ applications depends on being able to drive one
application into the view of a different application.
In
contrast, in the S60 view, persistence rules positively discourage
leaving a view in the state of its last use or switching to an external
view. Instead, S60 takes the approach of embedding one application
instance within another to achieve a similar effect. S60 applications
rarely have their state affected by another application, whereas in UIQ
this is the norm.
An important point to remember is that, just as the classic version of this pattern relies on Observer [Gamma et al.,
1994] to allow the controller to react to events generated by the view
and the view to reflect the application state stored in the model, the
Symbian OS version of this pattern also relies on an event-driven
programming approach to keep the three main classes aligned whilst
allowing them to remain largely decoupled. If you introduce your own
model class, you should ensure that you build on this approach to avoid
introducing hard dependencies that will reduce your ability to port your
application in future.
2.3. Implementation
Overview
There is no such thing as a
generic Symbian OS application, because at a detailed level application
design depends on specific features of GUI variants. However, at a
fundamental level the underlying Symbian OS frameworks impose a common
blueprint on all applications irrespective of UI variant:
The GUI variant
framework implementations derive from the underlying Symbian OS
frameworks and therefore share a common structure across all devices.
While
specific details of GUI behavior differ between UI variants, the
significant differences are encapsulated in those variants and come 'for
free' with the use of the specific application frameworks for the
associated devices.
The
main differences between the UI variants are the details of resource
file usage and application lifecycle implementation details. This is in
addition to more obvious differences to the application's own UI that
reflect the 'look and feel' of the UI variant such as view layout and
navigation, menu layout and behavior and the layout of
application-independent screen areas, which includes the general status
and task bars that are always shown by a device.
The
independence of Symbian OS from the GUI variant running on top of it
encourages the partitioning of application code between GUI-independent
application engine code and GUI-dependent display and user interaction
code.
The Symbian OS application
frameworks are hidden from concrete applications by the application
frameworks of the GUI variant for the device you are currently
targeting. Beneath the GUI variant, Symbian OS defines the following
levels of UI and application frameworks:
Uikon
is the most abstract framework. It defines the generic application
framework, free of any 'look and feel' policies, from which GUI variants
derive a concrete framework implementation. It includes base classes
derived from, as well as concrete classes used by, the derived
application frameworks.
Application Architecture
provides a less abstract implementation of Uikon interfaces that define
the UI-independent application lifecycle and responsibilities,
including the relationships between applications, data, and resources.
Control Environment provides a less abstract implementation of Uikon interfaces to supply base classes and an environment for abstract UI controls.
These controls are window-using, possibly nested, rectangular screen
areas that accept user input and system events. In particular this
framework provides abstract interfaces for graphics contexts, the
windowing model, and event dispatching.
All Symbian OS
applications derive their basic application classes from GUI variant
base classes, including (from v9) application view base classes. Table 1
shows a mapping between the base Symbian OS framework classes, the GUI
variant base classes used by applications and the classic MVC model.
As Table 1
shows, there is no immediate equivalent for the Symbian OS Application
class in the classic MVC model and MVC views can have a number of
alternative implementations in Symbian OS applications.
Table 1. Model –View –Controller Inheritance Hierarchy
S60 Base Class | UIQ Base Class | Symbian OS Base Classes and Associated Framework | Classic MVC Equivalent |
---|
CAknApplication | CQikApplication | CEikApplication (Uikon)CApaApplication (AppArc) extends | N/A |
CAknDocument | CQikDocument | CEikDocument (Uikon)
CApaDocument (AppArc) extends | Model |
CAknViewAppUi and, for non-view-based applications, CAknApUi | CQikAppUi | CEikAppUi (Uikon)CCoeAppUi (CONE) extends | Controller |
CAknView and, for non-view-based applications, CCoeControl
NB: All S60 view base classes are control-owning, i.e. they own an instance of a CCoeControl. | CQikViewBase (for 'single page' views), CQikMultiPageViewBase (for 'multi-page' views), and CQikViewDialog (for simple 'dialog' views).
NB: All UIQ view base classes are CCoeControl derived i.e. they are controls. | CCoeControl (CONE) | View |
The following
code fragments demonstrate the degree to which a Symbian OS application
follows a generic structure, irrespective of the GUI variant it targets.
The variations between S60 and UIQ certainly cannot be ignored and in
some cases are quite subtle. They can, however, be quite easily
identified and isolated.
Application
The top-level application class binds the application into main application frameworks and hence into the application lifecycle.
In the code for an
Application class, the only difference between S60 and UIQ is the base
class derived from. In either case, you still need to implement the
entry point that the framework uses to launch the application as well as
a method to return the application UID. This class is also responsible
for the next object in the chain, the application Document, whether or
not it is seriously used.
class CMyApplication : public // CAknApplication OR CQikApplication
{
public:
// Return Application's UID
TUid AppDllUid() const;
protected:
// Create the document
CApaDocument* CreateDocumentL();
private:
// Called by the framework to create a new application instance
CApaApplication* NewApplication();
};
Document
This class was intended to
encapsulate the application data and core logic however it is usually
not seriously used though it still needs to be provided to allow the
application to interact with the application frameworks. The class is
responsible for creating the next object in the chain, the AppUi.
If it is used in earnest, such as by a file-based application, the
document reads its initial state from a file. Again the only difference
between S60 and UIQ is the base class derived from.
class CMyDocument : public // CAknDocument OR CQikDocument
{
public:
// Constructors / destructor omitted
// Create the App UI
CEikAppUi* CreateAppUiL();
};
AppUi
The AppUi provides
the application-specific handling of events given to it by the
application frameworks as a result of system events such as those
generated by the end user. It is generally responsible for creating the
application's model and drives it as a result of these events.
In addition, the AppUi
provides the UI classes and defines the commands which are called from
the menu choices command switch handler. It does this by creating the
main default view and registers it with the framework although it is not
expected to destroy the view.
The responsibilities of the AppUi class differ between S60 and UIQ. In S60, the AppUi
is responsible for command handling, which follows the original Symbian
OS pattern; in UIQ3, a new command-handling framework moves
responsibility for command-handling to view classes, enabling commands
to become view-specific.
class CMyAppUi : public // CAknAppUi OR CQikAppUi
{
public:
// The application frameworks requires the default C++ constructor to
// be public
CMyAppUi();
...
private:
// The AppUi owns the main application view
CMyAppView* iAppView;
private:
// S60 apps only, cf. new UIQ command-handling frameworks
void HandleCommandL(TInt aCommand);
void HandleStatusPaneSizeChange();
};
View
S60 and UIQ diverge most
significantly in the way that they handle views, reflecting important,
but subtle, differences in the underlying treatment of views and, in
particular, of view switching. In UIQ, Direct Navigation Links
enable applications to drive views belonging to other applications; in
S60, on the other hand, applications typically collaborate using
embedding. Both view derivation and view responsibilities and behavior
are different between these GUI variants.
The following is a conventional S60 view class, which is derived from CCoeControl; more recent S60 view classes differ in that they are not control derived, but do own controls:
class CMyS60View : public CCoeControl
{
public:
// Constructors/destructor omitted
// Handle drawing and size changes
void Draw(const TRect& aRect) const;
virtual void SizeChanged();
};
The UIQ application view derives from one of the new UIQ view base classes which are themselves derived from CCoeControl:
class CUIQTeomplateView : public CQikViewBase
{
public:
// Constructors/destructor omitted
// Return a globally unique view id that can be used to DNL to this
// view
TVwsViewId ViewId() const;
// Commands are handled in the context of a view
void HandleCommandL(CQikCommand& aCommand);
};
2.4. Consequences
In practice, almost any
application written for Symbian OS that has a UI is obliged to use this
pattern. The possible exceptions are special case dialog-based
applications that require only a very simple dialog-like interface,
which both S60 and UIQ support; for example, settings management and
other 'single function' applications. Even in these cases however, the
application must implement a simplified version of the standard
application pattern.
This means that there is no real question of a trade-off for
applications between using it and not using it. It is, however, useful
to understand the influence it has on your application.
Positives
Development
costs are reduced as the strong framework support allows applications to
follow a standard template with minimal custom code to behave as a
fully integrated, well-formed application.
Maintenance
costs are reduced because of both the reuse of the applications
frameworks proven in many releases of Symbian OS and the use of a
well-known design pattern.
Porting
of applications between GUI variants is made easier through the
isolation of UI dependencies from core application functionality which
makes it easier to reuse the same core application logic across multiple
GUI variants based on Symbian OS.
Testing
costs are reduced as the high degree of decoupling between the
individual MVC components allows each to be easily isolated and unit
tested.
Clear and structured application designs are enforced.
Negatives
For developers new
to Symbian OS, especially those unfamiliar with object-oriented and
framework programming, there is an inevitable overhead in learning how
to implement applications using this pattern. Especially as this pattern
differs from the classic implementation.
2.5. Example Resolved
For a full set of example code on how a contacts application can be created, please see:
<Nokia S60 3rd Edition SDK Installation Directory>\S60Ex\Contacts
UIQ
3 SDK » UIQ Developer Library » UIQ Examples » QLayout – this describes
an example application that has a view similar to that of a contacts
application.