One of the marquee announcements at this year’s Dreamforce was the introduction of the Salesforce Touch Platform. The Touch Platform is a comprehensive set of developer tools and platform features that let enterprises build mobile applications quickly and securely in the cloud. To help developers better understand the Touch Platform Marcus TorresAndrew Waite (senior Product Managers for the Touch Platform) and myself developed a sample/reference mobile application called Cloud Hunter. I demoed and dissected this sample application in my Developing Hybrid Apps with the Salesforce Mobile SDK Dreamforce session and this is the first in a series of blog posts in which I’ll describe the application architecture and features in (gory) detail. You can also peruse the entire code base for Cloud Hunter in this GitHub repo.

Use Case

Cloud Hunter is a scavenger hunt application in which players compete to finish various ‘missions’. The application supports four basic types of missions. Check-in type missions require the player to ‘check-in’ with their phone at or near a particular location/landmark (say Moscone West). Signature type missions require players to get the signature of some designated luminary (i.e. someone other than myself). Scanner type missions require the player to scan a specific bar or QR code (say the QR code embedded in someone’s Dreamforce conference badge). Finally, Camera type missions require players to capture a specific picture (say of Sassy). Each mission has a maximum number of points that a player can earn, but the actual number of points earned depend on how quickly the mission is completed (i.e. the quicker you complete a mission, the more points you earn).

Application functionality

This short 2 minute demo video shows the Cloud Hunter application in action. You can change the video quality to 720p(HD) for optimal viewing.
//www.youtube.com/watch?v=Kx-CLA6gm6w&hd=1

After the user launches Cloud Hunter on their iOS/Android device for the first time, they’re required to login to Salesforce. Once logged in, the user is presented with three main ‘screens’. The ‘Player’ screen displays the user’s picture (from the corresponding Chatter profile) and current point total. The ‘Standings’ screen shows all the players that are currently playing the game and their respective point totals. The heart of the application is the ‘Missions’ screen. This screen lists all the missions that are part of the scavenger hunt. Each mission can be one of the four supported types described earlier and clicking on a particular mission displays the mission detail screen where the player can complete the mission and earn points.

Application Architecture

Cloud Hunter was developed as a hybrid application using the Salesforce Mobile SDK. To better understand the differences between native, web and hybrid mobile architectures, you can watch this Dreamforce session recording and/or read this wiki article. We chose a hybrid architecture for Cloud Hunter for two primary reasons. One, we wanted to support Cloud Hunter on both iOS and Android but didn’t want to create two separate native apps. Second, Cloud Hunter required certain device access (like the camera) that is not possible with a pure web application. Hybrid apps are developed using standard web technologies like HTML5, JavaScript, CSS3 etc. (unlike native apps that require Objective-C, Java etc.), but are then ‘wrapped’ in a thin native container to access device functionality like the camera, microphone etc. Cloud Hunter was developed entirely on the Salesforce Touch Platform using Visualforce and Apex and we then used the hybrid version of the Salesforce Mobile SDK 1.3 to create Android and iOS versions of Cloud Hunter.

Data Model

This ERD shows the data model for Cloud Hunter. The central custom object for the app is Game. Each scavenger hunt is a new Game record. Each Game has one or more child Mission records (via a lookup relationship). As described earlier, each Mission can be one of four basic types. Each Salesforce user that signs into the application on their mobile device and joins a hunt (aka Game) creates a new Player record. Every time that a Player successfully completes a Mission, a new Achievement record is created (i.e. Achievement is a junction object between Player and Mission).

Code walkthrough

The rest of the blog series will focus on the MissionList and Mission Visualforce pages that display the mission list and detail screens respectively. Lets first look at the abridged markup for the MissionList page.

<apex:page docType="html-5.0" standardStylesheets="false" showHeader="false"
           controller="MissionListController" title="Missions" cache="false">
<apex:composition template="Template">
<apex:define name="title">Mission List</apex:define>
<apex:define name="head">
    <script type="text/javascript">
        var missionRecs = {};

        $j('#currentPage').live('pageinit',function(event){
            getMissionItems();
        });

        function getMissionItems(callback) {
            //Invoke JS Remoting function to retrieve Mission Items
            //MissionController.getMissionList(
            Visualforce.remoting.Manager.invokeAction(
                '{!$RemoteAction.MissionListController.getMissionList}',
                '{!gameId}',
                function(records, e) {
                    ....
                    showMissionList(missionRecs, callback)
                },
                {escape:false}
            );
        }       

        function showMissionList(records, callback) {
            $j('#missionList').empty();
            var x = 0;
            $j.each(records,
              function() {
                var newLi = $j('<li></li>');
                ....
                newLi.appendTo('#missionList');
                x++;
             });
             ....
        }
        ....
    </script>
</apex:define>
<apex:define name="header">Your Missions</apex:define>
<apex:define name="content">
    <div data-role="content">
        <ul id="missionList" data-filter="false" data-inset="true" data-role="listview"
                data-theme="c" data-dividertheme="b">
        </ul>
    </div>
</apex:define>
</apex:composition>
</apex:page>

The first thing of note is the use of the docType attribute in the <apex:page> tag. This is how you mark a Visualforce page as being HTML5 compliant (vs the default, which is HTML 4.01). It is highly recommended that you use HTML5 for mobile web development in order to use features like the application cache (for offline access), geolocation and more.

Next, note the use of Visualforce templates in this page (the <apex:composition> and <apex:define> tags). As seen in the demo video, all the pages in this application share a common header and footer. VF templates is a great way to enforce a common look and feel across your application and Cloud Hunter uses a common template (creatively named Template.page) for all its pages. In addition to including the common header and footer visual elements, the template page also centralizes all the necessary JavaScript and CSS imports for this application (e.g. JQuery Mobile).

The page uses the popular JQuery Mobile (JQM) framework for rendering its content. JQM specifics are outside the purview of this post, but what is noteworthy here is how the page combines JQM with JavaScript Remoting to query and display the list of missions. JavaScript Remoting is a feature of VF that allows you to invoke controller/extension methods directly from your JS code. Line 16 shows how this page invokes the getMissionList method on the MissionListController class using JavaScript Remoting. Note the syntax of the JS Remoting call here, namely the use of Visualforce.remoting.Manager.invokeAction and $RemoteAction. The advantage of using the $RemoteAction syntax is that if this page and the associated controller class is added to a managed package and assigned a unique namespace, the call above will continue to work without any changes to the code. If however you were to use the alternative syntax shown on line 15, you’d have to change the page markup to include the package namespace (i.e. namespace.MissionController.getMissionList).

The rest of the JS code is fairly straightforward. Once the list of mission records is returned by the JS remoting call, the showMissionList method is invoked, in which we iterate over the mission records and dynamically add new <li> list elements into the page DOM.

Switching over to the server-side, here is a small snippet of the getMissionList method that is being invoked from the MissionList page.

public with sharing class MissionListController {

    public class Mission{
        public Mission__c mission {get;set;}
        public boolean completed {get;set;}
        public Decimal pointsAwarded {get;set;}

        public Mission(Mission__c m){
            mission = m;
            completed = false;
        }
    }
    .....

    @RemoteAction
    public static List getMissionList(String gameId){
        List missions = new List();

        // Secondary list to de-prioritize the completed missions.
        List completedMissions = new List();

        Map completedMissionAchievementIdMap = new Map();

        for (Achievement__c a : [SELECT Mission__c, Points_Awarded__c
                                   FROM Achievement__c
                                  WHERE Player__r.Game__c = :gameId AND
                                        Player__r.User__c = :UserInfo.getUserId()]) {
            completedMissionAchievementIdMap.put(a.Mission__c, a);
        }

        for (Mission__c m : [SELECT Id, Description__c, Name, Current_Point_Value__c,
                             Max_Point_Value__c,..
                             FROM Mission__c
                             WHERE Mission_Start_Time__c < :datetime.now() AND
                             (Mission_End_Time__c > :datetime.now() OR
                             Mission_End_Time__c = null) AND
                             Game__c = :gameId
                             ORDER BY Current_Point_Value__c DESC ]){
            Mission mission = new Mission(m);
            if (completedMissionAchievementIdMap.containsKey(m.Id)){
                mission.completed = true;
                mission.pointsAwarded =
                      completedMissionAchievementIdMap.get(m.Id).Points_Awarded__c;
                completedMissions.add(mission);
            } else {
                missions.add(mission);
            }
        }

        // Add all the completed missions to the end of the list.
        missions.addAll(completedMissions);
        return missions;
    }
}

The @RemoteAction annotation allows the method to be invoked from JavaScript on a Visualforce page. The method also has to be public/global and static per the JS Remoting requirements. In the getMissionList method we’re querying for all mission records that are part of the current game and also determining which of those missions the logged in user has already completed (via a query to the Achievement object). The result is returned to the page as a collection of the Mission wrapper class.

Seasoned Visualforce developers will notice how this page design eschews the use of standard Visualforce components like <apex:dataTable>, <apex:pageBlock> etc. Instead, the page uses standard HTML tags like <div> and does its dynamic data binding using JavaScript and JavaScript Remoting. This pattern is ideal for mobile web app development since you avoid some of the CSS and JavaScript libraries and page structure that come with the standard Visualforce tag library and that are really optimized for a desktop environment. For mobile pages, you want to avoid the <apex:form> tag in particular since it comes with a viewstate that is not always ideal for a resource constrained environment like a mobile browser.

Hopefully this post has laid the foundation for how and why we built Cloud Hunter on the Salesforce Touch Platform. In subsequent posts, I’ll explore how we converted the Visualforce based web mobile app described above into hybrid Android and iOS applications using the Salesforce Mobile SDK. I’ll also explore how we implemented some of the specific app functionality like taking a picture, signature capture, bar/QR code scanning and geolocation. Stay tuned for those posts, but in the meantime, comments/questions are welcome.

tagged , , , , , Bookmark the permalink. Trackbacks are closed, but you can post a comment.
  • http://twitter.com/altius_rup Rupert Barrow

    Good stuff !
    Thanks for the great article, can’t wait for the sequel !
    Rup

  • bhagat bisht

    Sandeep I tried to install this app but when i click scan button to scan QR code it prompt gap_callbackserver, gap_poll messages only. Why it might be happening?

  • Atif Mohammed

    How to I install this app