Explore the structure of application state

Application state is a hierarchical tree of properties and values which resides on both the service and the client. PureWeb uses it under to hood to maintain state synchronicity.

You can use application state as a simple data store. For instance, you could use it to store usernames, URLs, or the level reached by a player in a game, You can also use it to integrate service functionality into the client: if the user selects a checkbox in the client, you could change the state of the checkbox in application state to "selected", which would notify the service to trigger the functionality of that checkbox.

The format of application state is very flexible; it can contain basically anything that can be stored as an XML element or collection of elements. There are some basic path notation and naming conventions to follow, which we'll explore in this section, but otherwise you are free to structure the information as you see fit.


The basic structure

The basic data structure is a simple XML document consisting of nested tags and values. By default, the application state tree contains only the root element and the <PureWeb> element:

<ApplicationState xmlns:typeless="http://calgaryscientific.com/typeless/2008">
    <PureWeb>
       // PureWeb-specific data
    </PureWeb>
</ApplicationState>

The <PureWeb> element is entirely maintained by the APIs under the hood. It includes, for example, details about the running service application, the session IDs that identify the host and the participants in a collaboration session, and other bookkeeping information that is needed to manage the solution.

You can read from the <PureWeb> element, but to prevent conflicts, it is strongly recommended that you do not write to this element or its children.


User-defined elements

Application state supports user-defined elements as well. For example, the Scribble sample application stores the pen color in application state, and the Asteroids sample application stores the game level reached by the player.

You can add as many elements as suits your needs (it is not unusual to have large numbers), and structure their hierarchy in the way that makes the most sense for your application.

A common approach is to base the hierarchy on the layout formation of the service application. For example, let's say that your service has a main page which contains a tab with several controls. In this scenario, the application state path targeting a control within a particular tab could be structured as follows:

/MainPage/TabX/ControlName

Application state paths can go as deep as necessary to uniquely identify the location of a particular control.

The decisions you make about the structure of application state matter, and making the wrong choices can result in the need to redo some work, or create issues that are hard to diagnose. Therefore, it is worth investing some time planning your tree at the onset of a project. Below are some pointers that may help with this effort:

  • Before getting started, it is useful to have a basic understanding of how PureWeb keeps application state synchronized under the hood.
  • Decisions regarding whether certain elements should be organized as parent/child, or as siblings, can impact how state change handlers work. The change handlers topic has more information.
  • For sensitive data, session storage may be a better option. This is because application state is the same on the service and all connected clients in a session, and so collaboration participants will have access to this data. Session storage allows you to maintain private synchronized data stores individually for each user session.
  To help you fine tune your application state tree, you can attach a Diagnostics Panel to your client. The panel provides an "AppState" tab, which allows you to view the client's application state tree in real time.

Naming conventions

When naming your elements, follow the usual naming conventions for XML elements. For example, the first character in the name can be a letter or underscore but not a number, and names must not contain spaces or other punctuation characters.

For more information, see this section of the W3C recommendation.


Path notation

Whenever you read from or write to an element in application state, you must specify the path of this element in the tree hierarchy. When you provide this path, there are some basic notation conventions to follow:

  • Provide the entire path, except for the root ApplicationState element, which should be omitted.
  • Divide each level using a forward slash.

For example, let's say that you want to read the value of the DefaultColor element in the tree below (session IDs have been reduced to two-digit numbers to make this example more readable):

<ApplicationState xmlns:typeless="http://calgaryscientific.com/typeless/2008">
    <PureWeb>
        <Collaboration>
            <OwnerSession>01</OwnerSession>
            <Sessions>
                <SessionId-01>
                    <DefaultColor>#FF0000</DefaultColor>
                </SessionId-01>
            </Sessions>
        </Collaboration>
    </PureWeb>
</ApplicationState>

You would refer to the element as follows:

/PureWeb/Collaboration/Sessions/SessionId-01/DefaultColor

 

To eliminate the need to remember paths, and to save some typing, it is common practice to use variables. You can store an entire path as a variable, or use variables to dynamically build paths, for example:

'/PureWeb/Collaboration/Sessions/' + SessionId + '/DefaultColor'

You can also use condition operators, for example:

  • /Definitions/#1
    Would return the first ordered child of an element called Definitions
  • /Definition[Id=1;Type=Button]
    Would return a Definition element with Id of 1 and of type Button

Under the hood

Changes to application state, whether made directly or through change handlers, are not sent at once. Neither are they added to a buffer/queue that gets sent periodically.

Rather, application state is synchronized by regularly polling the client-side tree for differences, and if there are any, sending a command to the service to merge these differences into the service-side tree. Sampling of state differences is done every 16 ms, and so PureWeb can send up to 60 state differences per second.

This has a few implications:

  • Because there is no queue to track the order into which changes handlers are called, the ordering of the callbacks cannot be guaranteed. See the section about change handlers for details.
  • If you make a rapid series of changes to a particular state value, you are guaranteed to see the final value, but if the state changes happen faster than the sampling interval, you may not see all the intermediate values.