Tuesday, March 8, 2011

WURFL-based mobile device detection for Sitecore CMS

Mobile web browsing is so popular nowadays, that I was confused by the fact that there is no plug'n'play mobile detection solution for Sitecore and many people still use odd code like "if(userAgent.Contains('iPhone')...".
The best mobile device database is WURFL, and it has a great .NET API from 51 Degrees... so what are we waiting for? Let's create a new module!
The solution should be applicable to any Sitecore project,  and at the same time simple and customizable.
Here is the plan:
  • Use WURFL to detect device
  • Extend Device template (add additional fields for configuration)
  • Extend DeviceResolver class (to take make sure it takes additional fields into account)
  • Let the user override detected device
In this solution, I'll use WURFL wrapper from 51degrees - "Foundation" version is free and contains all required tools.

It can be easily integrated to the site by adding entries to config and adding the following code to global.asax:

public void Application_Start() {
    HttpCapabilitiesBase.BrowserCapabilitiesProvider = new FiftyOne.Foundation.Mobile.Detection.MobileCapabilitiesProvider();
  }

WURFL properties will be accessible using the following code:

HttpContext.Current.Request.Browser["%wurfl property name%"]

By default, Sitecore CMS uses only UserAgent and QueryString to resolve device, Device template contains the following fields:


We can add more field like "screen width", "is mobile device", but will it be customizable and extensible? Fortunately, we have Rules Engine, that can be easily configured and helps avoid additional code, that would handle situations like "if screen width is... else...".
Actually, after creating the initial prototype, I've found blog post with exactly the same idea - Using the Sitecore Rules Engine in a Custom Context: Setting the Context Device, it contains more info about Sitecore device resolving and a lot of useful links - definitely worth reading if you're working with devices.

So, let's add a field of type "Rules" to the template:


The name of the field is "Conditions' as it will contain conditions only, the action is setting Context.Device to the current item. The following code will be used to evaluate rules and find best-matching device:

DeviceItem[] all = database.Resources.Devices.GetAll();

            foreach (var device in all)
            {
                var ruleContext = new RuleContext();

                foreach (Rule<RuleContext> rule in RuleFactory.GetRules<RuleContext>(new[] { device.InnerItem }, "Conditions").Rules)
                {
                    if (rule.Condition != null)
                    {
                        RuleStack stack = new RuleStack();
                        rule.Condition.Evaluate(ruleContext, stack);
                        if (ruleContext.IsAborted)
                        {
                            continue;
                        }
                        if ((stack.Count != 0) && ((bool)stack.Pop()))
                        {
                            return device;
                        }
                    }
                }
            }

Now, let's create a set of conditions that will be used for determining appropriate device. The list is pretty small, but it can be easily extended:


Here are the few examples of how the conditions above can be combined:

1) Apple's iPhones

2) And tablets

3) More complex query

By the way, mobile device detection rules can be also used for conditional renderings. It is possible to provide amazing browsing experience to the visitors - show the nearest service center depending on brand, or suggest using mobile application if device is iOS / Android, etc. 
In order to configure Mobile Device Detector in your solution, you need to perform the following steps:

1) Install the module package. It contains template updates, conditions, class library, WURFL data file, etc.
2) Update Global.asax and Web.config according to instructions received during the installation.

The module was tested on Sitecore 6.4 (Update 1). Please make sure that your site Application Pool NET Framework Version is set to "4.0".

And one more thing about the solution. In order to make it possible to implement "switch to the normal website version"  button, I've added functionality(based on session-level cookies) for persisted device switching. If you switch to another device using "sc_device=%name of the device%" and add "&persisted=true" parameter, the device will remain switched until you reopen the browser.

The goal of this project is a complete mobile device detection solution, applicable to any Sitecore website. Please share your ideas on how to improve this module, I'll collect feedback and update it. Thanks in advance!