Strategies for Mobile Applications and Sitecore Part 2
This is Part 2 of our blog post about how we used Sitecore to make a Mobile API. To recap, this single API is used to drive 4 different devices, iPhone, iPad, Andriod, and Blackberry. In the first part we covered the highlights of Design Patterns, the RESTful API approach, as well as how we configured Sitecore to support our Mobile API. This last point included Sitecore Devices, Layouts, and Content Tree Structure.
In Part 2 we’ll be looking at more technical issues such as how we build the API codebase in Visual Studio, the XML and JSON serialization components, and the scenarios our caching strategy covers.
The API we built in Visual Studio can be grouped into two areas: the Model classes and the Sublayouts.
As you likely know, a Model class (sometimes called an Entity Class), contains all the properties that define a single Object. We’ll have a model class for each type of object we want to send back to our mobile apps. For example, there is an Article class, an Expert class, and a Topic class.
These models are then serialized into either JSON or XML. To accomplish the serialization we use features that area already part of the .NET Framework. Namely DataContractSerializer and DataContractJsonSerializer. These two classes rely on Reflection to identify serializable classes and methods using attributes called DataContract (for the class) and DataMember (for the properties.)
One common question from folks who use the Custom Item Generator is: why not just use the generated classes as our Models?
A little background on the CIG: The goal of the Custom Item Generator is to facilitate agile development through code generation. Each custom item class is a reflection of the corresponding template found in Sitecore. As changes are made to a template in Sitecore, the custom item file can be regenerated to show the new fields, new base classes, and any field updates.
However the information we send to our mobile apps necessarily needs to be fixed or it would require a client update on the mobile devices. For example, the mobile clients are expecting specific fields with specific names to be passed through via the JSON. The code generation of the custom item generator makes it really easy to rename or remove fields which has the huge impact of breaking the data contract between Sitecore and the mobile devices. So we keep these model classes to expose just the data that is need for the mobile apps in an easily serializable way as well as for maintaining that data contract. There are other ways to accomplish the same goal, (maybe a Mobile API Interface), this way worked well for us.
Layouts and Sublayouts
The Mobile API layout and sublayouts, as we saw, are plugged into the Mobile API Device. For our case, we know we’re going to be returning something more data oriented like JSON or XML so we don’t need much in presentation. Therefore our Layout file is very simple and only contains a Placeholder for the Sublayout.
For the Sublayouts, there is one for each request type. In our example we’ll be looking at segments from the Article sublayout. Each sublayout has a couple common responsibilites: they should a) populate the equivalent Model Class, and b) issue the JSON/XML Response. For issuing the Response, let’s look at the following segment:
This snippet deserves a little discussion because it has some elements we haven’t talked about yet. First, our mobile apps have the option of including a date with the request representing the last time that mobile app issued that request. The idea is that if the mobile app has issued that request before and the content on the server hasn’t changed, then the mobile app is allowed to use its local cache. Let’s explain:
In the first condition, if there is no date (LastFetchValue == null) or if the date is more recent than the last time the content was updated (represented as ‘mostRecentUpdateUtc’), then we will execute the serialization normally and return that in the response.
However, if there was a date, and it is more recent than the last time the content was updated, then we have the opportunity for some performance gains. We will skip the serialization (gain 1); return something very small, a “Not Modified” response code (gain 2); which then tells the mobile app to use its local cache (gain 3.)
Base Class: ApiResponder
Because the goals of each Sublayout are similar, there is a benefit to having each sublayout inherit from a base class which can implement common actions. Our base class is called ApiResponder. This baseclass shares its implementation with the Sublayouts for the following two areas:
- a) execution of the JSON/XML Serialization
- b) cache management (which we haven’t talked much about yet.) The following is a segment from ApiResponder that handles the serialization:
In this code snippet we’re calling the appropriate DataContract method depending on which serialization format has been requested.
We’ve identified so far that there is a caching strategy involved. Let’s look more closely at what this means. First, some of the benefits we’ll gain from using caching:
- When we use a caching strategy it can result in fewer round trips to the server
- This implies less load on the server which increases scalability
- We gain faster page loads when using the local cache
- This implies a better user experience.
Local Cache DB
A local cache on a mobile device can be something as straightforward as a database record. Take this example which is essentially what we need: The record contains:
- The URL which for our RESTful API is the equivalent of the web method call.
- The Response: which for our app will contain the raw JSON/XML from the initial request
- The Date: this is what we use to record the date/time our response data was collected from the server.
Next, let’s look at the logic behind how we will use our cache. This somewhat complex looking Cache State Diagram can be summarized in 4 basic use cases. But first to understand it we need to introduce the concept of the cache trust period. That is to say, suppose our period is 20 minutes, then our mobile app will trust the local cache for 20 minutes. Any requests for something in the cache that is older than 20 minutes is considered stale, requiring a request to Sitecore.
#1: The user requests from the mobile app an Expert page for the first time.
Because it’s the first request, there will not be a record in the local cache. Our flow will go to Sitecore. Because there is no date in our request, Sitecore will issue a serialized response of the Expert item. Our mobile app will store that response data in the cache table and render the view.
#2: The user requests the same Expert page 10 minutes later.
The response data will already be in our cache db, and because it is only 10 minutes old, it is under our 20 minute threshold. We can use the local cache and avoid reaching out to Sitecore entirely. Because we didn’t check with Sitecore we will not modify the date already in our cache record.
#3: The user requests the same Expert page 20 minutes later.
We are now 30 minutes after our initial request, or 10 minutes beyond our threshold. So the response data will be found in our cache table, but it is considered stale. Therefore we issue a request to Sitecore. In this use case, let’s assume that nothing has changed on this expert page within the last 30 minutes. Sitecore will now bypass the serialization and instead issue a 304 code as the result. Our mobile app, when receiving the 304 code, knows to use the local cache. And because we did confirm this content with Sitecore we will modify the date on our cache record.
#4: The user requests the same Expert page 30 minutes later.
Again, we are 10 minutes past our threshold for determining if our local cache is stale. Similar to use case #3 we will send our request to Sitecore. This time, let’s say that something did change on this Expert page within the last 30 minutes. Therefore Sitecore will execute the serialization and return that in the response. Our mobile app will update the local cache record with the latest data and a new date, and render the view to the user.
If you followed along, we covered each state represented in the diagram. Cool! We’ve also now described and implemented a very robust caching system that will improve the user experience, and decrease the server load.
Sitecore Publishing Strategies
Hiding Pages from Mobile Devices
There are some mechanics we can provide to make managing mobile content easier for content authors. One example is suppose our website has pages that are great for a desktop web browser but are less friendly for mobile devices. I’m looking at you, Mr. Flash Media Player. A simple checkbox in the Sitecore template can address this for the content author’s experience. And in the back end, we’ll want to implement code to ensure these pages are not discoverable on mobile apps.
Hiding Content from Mobile Devices
Or, if we want the page to be discoverable on mobile apps, but perhaps there is something on the page from a Rich Text Editor that isn’t going to render well, that can be addressed too. In this case we added a button to the Rich Text interface which inserts a not-mobile tag. In the codebase we add a Mobile Tag Resolver class which goes into the Rich Text pipeline. This class is responsible for removing the not-mobile wrapped content prior to serialization. Additionally there is a message attribute. Our content authors can add text here which the viewer would see instead of the original content. Something like, “This table has been removed from the mobile view. Please view this page on a desktop/laptop to see the table.”
In addition to flash content, examples of things that may be removed include large images and complex table layouts.
Additional Strategies to Consider
Identify a target OS and Feature set
In contrast to simply building a Blackberry app, identify the minimum version and OS that this app will support. It will greatly simplify the requirements and codebase when there isn’t a need to support every possible scenario. The further back we go, the harder it is to maintain, obviously. Take for example a web application that is required to support IE6 vs one that only needs to provide support for IE7/8. Enough said.
Render images asynchronously
Note that in our JSON/XML we are not passing the images themselves, but rather we are passing the location of the image. This means that we’re still going to need to get those images. A good strategy here is to go ahead and load as much of the view as possible, while in the background retrieve and load in the images asynchronously. The benefits of getting the user to their content as fast as possible goes without saying, and this one way to improve the user experience.
Keep the user within the app for internal links
In rich text content, authors have the flexibility to add both external links to pages out in the webiverse and internal links to other Sitecore based content. If our mobile app supports internally linked content, such as Article and Expert pages, then the worst thing to happen would be to have the user click that link and see it opened in the mobile device’s browser. Ideally what we want is to keep the user inside the context of the mobile app. Our method here was to append api=1 to the end of internal URLs prior to returning the response. This will be the indicator our mobile app uses to keep the rendering with the mobile app.
There are a number of different strategies we looked at to conceptualize the Mobile App integration with Sitecore.
RESTful API: We looked at the specifics of what it means to be RESTful as well as relating this to design patterns and their benefits.
Sitecore Configuration: We looked at how Sitecore was configured using standard Sitecore features in order to facilitate the use of the RESTful API concepts and coordinate the codebase.
The Code: We looked first at Model classes which would become serialized and represent our response data. And we looked at the Sublayouts and their shared base class, the API Responder which was responsible for populating the Models, executing the serialization, and managing the cache.