Quick poll before we begin. What device are you reading this blog post on? Desktop? Smartphone? Perhaps an iPad? A Blackberry Playbook? (just kidding!) I happen to be lucky in that I don’t have to do anything different in my writing to support all these different devices and form factors.
Its not that simple however if you’re building a modern Web application for the Social Enterprise. We’re living in a post-PC world where in the most recent quarter, Apple’s total revenue from its iPhone business alone (not even counting iPads) was greater than Microsoft’s total revenue for the quarter. With the proliferation of mobile devices in this post-PC era, applications now have to support a variety of platforms (e.g., iOS vs Android), form factors (phone vs tablet vs desktop vs Samsung Note) and device capabilities (e.g., touch enabled vs not).
So what are some of the key considerations and design options for Force.com developers looking to develop such device-independent applications? I think that it comes down to answering three fundamental questions, which are, in order:
- Which devices and form factors should your app support?
- How does your app detect various types of devices?
- How should you design a Force.com application to best support multiple device types?
Lets tackle each of these questions in turn.
Which Devices and Form Factors Should Your App Support?
The answer to this question is dependent on your specific use case and end-user requirements. It is, however, important to spend some time thinking about exactly which devices, platforms, and form factors you do need to support. Where you end up in the spectrum of ‘Support all platforms/devices/form factors’ to ‘Support only desktop and iPhone’ (as an example) will play a major role in how you answer the subsequent two questions. As can be expected, important trade-offs have to be made when making this decision. Supporting multiple form factors obviously increases the reach for your application. But, it comes at the cost of additional complexity both in terms of initially developing the application, and maintaining it over the long-term.
Developing true cross-device applications is not simply a question of making your web page look (and perform) optimally across different form factors and devices (desktop vs phone vs tablet). You really need to rethink and customize the user experience for each specific device/form factor. The phone or tablet version of your application very often does not need all the bells and whistles supported by your existing desktop-optimized web page (e.g., uploading files or supporting a use case that requires many distinct clicks). Conversely, the phone/tablet version of your application can support features like Geolocation and taking pictures that are not possible in a desktop environment. There are even significant differences between the phone and tablet versions of the better designed applications like LinkedIn and Flipboard (e.g,. horizontal navigation in a tablet version vs single hand vertical scrolling for a phone version). A little closer to home, touch.salesforce.com is another great example of a user experience that is customized for a specific form factor. Think of all these consideration and the associated time and cost it will take you to support them when deciding which devices and form factors to support for your application.
How does your app detect various types of devices?
Once you’ve decided which devices to support, you then have to detect which device a particular user is accessing your Web application from (in order to present a web page customized for that device/form factor). Before addressing this issue, I want to point out an important assumption that I’ve made in the writing of this blog post. I’ve assumed that the phone or tablet version of your application will be a web (aka HTML5), or hybrid (HTML5 wrapped in a thin native container) mobile app. For a deeper dive into the native vs web vs hybrid approach to mobile development, check out the session recording from last year’s Dreamforce. The good news for Force.com developers is that Salesforce Mobile SDK supports all three development approaches.
Back to the question at hand. Assuming a Visualforce based HTML5 application, how do you determine the device type of the client? There are broadly two ways of doing this:
Client-Side Detection
This approach uses JavaScript (or CSS media queries) running on the client browser to determine the device type. Specifically, you can detect the device type two different ways.
Client-Side Device Detection with the User-Agent Header
This approach uses JavaScript to parse out the User-Agent HTTP header and determine the device type based on this information. You could of course write your own JavaScript to do this. A better option is to reuse an existing JavaScript snippet like this simple example from the awesome HTML5 Rocks website or the JavaScript option from the detectmobilebrowsers.com website. A cursory search of the Internet will result in many more reusable JavaScript snippets that can detect the device type based on the User-Agent header. The same cursory search however will also expose you to some of the perils of using this approach. The list of all possible User-Agents is huge and ever growing and this is generally considered to be a relatively unreliable method of device detection.
Client-Side Device Detection with Screen Size and/or Device Features
A better alternative to sniffing User-Agent strings in JavaScript is to determine the device type based on the device screen size and or features (e.g., touch enabled). One example of this approach can be found in the excellent open-source Contact Viewer HTML5 mobile app that is built entirely in Visualforce. Specifically, the MobileAppTemplate.page includes a simple JavaScript snippet at the top of the page to distinguish between phone and tablet clients based on the screen size of the device.
Another option is to use a library like Device.js or Modernizr to detect the device type. These libraries use some combination of CSS media queries and feature detection (e.g., touch enabled) and are therefore a more reliable option for detecting device type. Here is a simple example of a page that uses the Modernizr library to accomplish this. A more complete example that uses the Device.js library and integrates with Visualforce can be found in this GitHub repo. Here is a snippet from the DesktopVersion.page in that repo.
The snippet above shows how you can simply include a <link> tag for each device type that your application supports (lines 6,7,8) and the Device.js library will take care of automatically redirecting users to the appropriate Visualforce page based on device type detected. There is also a way to override the default Device.js redirect by using the ‘?device=xxx’ format shown on lines 16 & 17. For more details on how to use Device.js to determine device type, check out the project home page.
Server-Side Device Detection
Another option is to detect the device type on the server (i.e., in your Apex controller/extension class). Server-side device detection is based on parsing the User-Agent HTTP header and here is a small code snippet of how you can detect if a Visualforce page is being viewed from an iPhone client.
Note that User-Agent parsing in the code snippet above is far from comprehensive and you should implement something more robust that detects all the devices that you need to support based on regular expression matching. A good place to start is to look at the RegEx included in the detectmobilebrowsers.com code snippets.
How Should You Design a Force.com Application to Best Support Multiple Device Types?
Finally, once you know which devices you need to support and how to distinguish between them, what is the optimal application design for delivering a customized user experiences for each device/form factor? Again, a couple of options to consider.
For simple applications where all you need is for the same Visualforce page to display well across different form factors, a Responsive Design approach is an attractive option. In a nutshell, Responsive design uses CCS3 media queries to dynamically reformat a page to fit the form factor of the client browser. You could even use a responsive design framework like Twitter Bootstrap to achieve this. As an example, check out this blog post describing a Facebook application I built on Heroku using Node.js and Bootstrap. As a developer looking to support multiple form factors, using Bootstrap/CSS media queries is an attractive option since you only have to develop a single Visualforce page that ‘magically’ works across various client devices. However, remember that this approach only really works for relatively simple UI requirements. This approach also contradicts one of the main recommendations from Question #1 – i.e., supporting multiple form factors/devices should be more than just reformatting a Web page. It should ideally also include device specific functionality (e.g., geolocation) and a customized user experience, which the Responsive Design approach does not address.
Another option is to design multiple Visualforce pages, each optimized for a specific form factor and then redirect users to the appropriate page using one of the strategies described in the previous section. Note that having separate Visualforce pages does not, and should not, imply code/functionality duplication. A well architected solution can maximize code reuse both on the client-side (by using Visualforce strategies like Components, Templates etc.) as well as the server-side (e.g., encapsulating common business logic in an Apex class that gets called by multiple page controllers). An excellent example of such a design can be found in the same open-source Contact Viewer application referenced before. Though the application has separate Visualforce pages for its phone and tablet version (ContactsAppMobile.page and ContactsApp.page respectively), they both share a common template (MobileAppTemplate.page), thus maximizing code and artifact reuse. The figure below is a conceptual representation of the design for the Contact Viewer application.
Lastly, it is also possible to service multiple form factors from a single Visualforce page by doing server-side device detection and making use of the ‘rendered’ attribute available in most Visualforce components (or more directly, the CSS ‘display:none/block’ property on a <div> tag) to selectively show/hide page elements. This approach however can result in bloated and hard-to-maintain code and should be used sparingly.
Hopefully, this discussion has been helpful in getting you on your way to developing cross-device HTML5 applications on the Force.com platform. If you have any other tips and tricks to share on the topic, please comment on the post and share it with the rest of the community. And now its time for me to get back in line at the local Apple store for my iPhone 5!