Fortunately, long time adopters of the WURFL API are in a much better situation. Virtual capabilities, first mentioned by me in the distant 2005 on the WMLProgramming mailing list are now finally introduced as a standard feature of WURFL API 1.5 on all platforms, released by the ScientiaMobile team last week. The release has taken a bit longer than expected, because of feature creep, but it was worth the wait, because of the cool new features that come with it.
There are mainly two new functionalities that WURFL adopters will appreciate in the new API: Capability filters and Virtual Capabilities. I’ll illustrate capability filters first, since they are the simplest to explain, before I delve into the complexity of virtual capabilities:
Once upon a time, a very old version of the WURFL Java API would ship with a webapp utility that allowed users to select a set of capabilities through a panel of checkboxes and request a wurfl.xml that only contained those capabilities. This was a manual process. Integrating it into the WURFl deployment cycle did not make a lot of sense. Starting with API 1.5, you can configure the OnSite API to just load the capabilities that are relevant for our application. This has obvious advantages in terms of memory footprint, that organizations may want to exploit. The actual syntax to define a filter varies from platform to platform (Java, .NET, PHP, InFuze,…), but it is essentially about providing a list of capability names. In the case of the Java API used with the Spring framework, configuration will look something like this:
important note: the new feature of virtual capabilities introduces dependencies on certain classic capabilities. If a filter is used, there is a set of physical capabilities that must be defined, under the penalty of the API refusing service. There has been some discussion internally and with beta-testers on whether this was the optimal strategy (we could, after all, trap the exception and keep going). In the end, we figured that forcing WURFL users to know about virtual capabilities and its dependencies would be consistent with the principle of least surprise. Experience shows that users are better served by a WURFL that refuses service when things are not OK, rather than one that forgives developers, but not the end-users of the application.
The actual syntax to define a filter for the different APIs is explained in the respective documentations.
As early as 2004, users of the WURFL API on the WMLProgramming mailing list would ask for this or that capability to be added to WURFL. Quite a few times the requested capability completely depended on another existing capability. Hence users would be better off computing the desired value out of the other capability, rather than introducing new capabilities which could quickly become inconsistent. After all, this is not very different from the Normal Forms familiar to DBAs in the RDBMs world. Already in 2005, there was this concept that popular dependent properties could still be offered by WURFL in the form of computed functions that behaved as capabilities, hence the name of virtual capabilities.
It was only in 2012 that the WURFL team finally had the cycles to go back to that idea, implement it and support it, sort of secretly and tentatively, in some of the APIs. In 2013, we decided to support the new feature across platforms and standardize it in all APIs. As an aside, here is a little lesson learned: features are like kids, once you bring them to this world, you have to support them. End of the aside.
Virtual capabilites are functions that behave like capabilities, but the value of which is a function of other capabilities and other values that come with the HTTP request. Virtual capabilities enable a few useful features today and open up to a world of new features in the future.
Virtual capabilities are called with a slightly different syntax than regular capabilities. Here is an example from Java (again, refer to specific platform documentation for details):
|Regular Capability||Virtual Capability|
WURFLEngine engine = (WURFLEngine) getServletContext(). getAttribute(WURFLEngine.class.getName());
Device device = engine.getDeviceForRequest(request);
log.debug(“Capability: ” + device. getCapability(“preferred_markup”));
|WURFLEngine engine = (WURFLEngine) getServletContext(). getAttribute(WURFLEngine.class.getName());
Device device = engine.getDeviceForRequest(request);
log.debug(“Virtual Capability: ” + device. getVirtualCapability(“is_smartphone”));
Important Note: Again, whether we should have gone for a unifying API for regular and virtual capabilities has been the topic of discussion internally in the team. In the end, we opted for different syntax for the two capabilities. Slightly steeper learning curve for users, but fewer surprises to deal with when things don’t work quite as expected further down the road.
So, what is so cool about these virtual capabilities? a couple of examples will make the point.
Example 1: is_smartphone
What constitutes a smartphone and what doesn’t is the source of endless discussions. Also, what one would call a smartphone in 2006, we would almost guarantee to be a legacy device by today’s standards. For this reason, we decided to introduce a new Virtual Capability called is_smartphone. This virtual capability implements a totally arbitrary definition of smartphone that may change in the future without notice to follow the evolution of the browser and device market. Think of it like a vienna sausage (AKA hot dog). It is easily available and many like it, but only as long as nobody tells them what the ingredients are. (I am tempted to argue that if you are wondering about what we put inside that capability, you may want to cook your own definition of smartphone with the other fresh unprocessed capabilities).
One hidden feature of this property is that phones for which the function evaluates to true are guaranteed to handle all popular Responsive Web Design (RWD) websites properly. This allows company to leverage is_smartphone (together with is_tablet and is_full_desktop) to detect legacy devices and route them to a different experience (say, an existing m.* site).
Example 2: advertised_browser, advertised_device_os, advertised_browser_version, advertised_device_os_version
A totally different, but still super-useful, use case is the one that introduces the ability to analyze the original UA string for browser and OS information. As you know, WURFL is geared at mobile devices. API 1.3 introduced better support for desktop web browsers, but still, filling the WURFL DB with information about hundreds of desktop web browser was never the way to go (particularly because of concerns with memory footprint).
advertised_browser, advertised_device_os, advertised_browser_version and advertised_device_os_version are virtual capabilities that solve this issue. When used to handle mixed mobile-web traffic, 98% or more of the desktop traffic will be mapped into the ‘generic_web_browser’ device or one of its descendants by the WURFL API. In this case, the 4 virtual capabilities can be used to extract OS and Browser version information through brute force UA string analysis. Of course, this could be done also previously outside of WURFL, but now we have decided to implement this as a standard supported feature of the WURFL API.
What follows is a complete list of virtual capabilities available with API 1.5:
|is_android||True if the device runs Android (any version).|
|is_ios||True if the device runs iOS (any version).|
|is_windows_phone||True if the device runs Windows Phone 6.5 or higher. Note that this does not include Windows Mobile or Windows CE.|
|is_app||True if the requests is from a native app. This type of request is from WebView components and RESTful API calls.|
|is_full_desktop||True if the requesting device has a full desktop experience. This capability is an alias for ux_full_desktop.|
|is_largescreen||True if the requesting device’s screen is of a high resolution (over 480 pixels in width and height)|
|is_mobile||True if the device is mobile, like a phone, tablet, media player, portable game console, etc. This capability is an alias foris_wireless_device.|
|is_robot||True if the request is from a robot, crawler, or some other automated HTTP client.|
|is_smartphone||True if the device is a smartphone. Internally, the matcher checks the operating system, screen width, pointing method and a few other capabilities.|
|is_touchscreen||True if the primary pointing method is a touchscreen.|
|is_wml_preferred||True if the requesting device should be served with WML markup.|
|is_xhtmlmp_preferred||True if the requesting device should be served with XHTML-MP markup.|
|is_html_preferred||True if the requesting device should be served with HTML markup.|
|advertised_device_os||Returns the operating system name of the requesting device. This works for mobile and desktop devices. (ex: “Windows”, “Mac OS X”)|
|advertised_device_os_version||Returns the operating system version of the requesting device. This works for mobile and desktop devices. (ex: “XP”, “10.2.1”)|
|advertised_browser||Returns the browser name of the requesting device. This works for mobile and desktop devices. (ex: “Internet Explorer”, “Chrome”)|
|advertised_browser_version||Returns the browser version of the requesting device. This works for mobile and desktop devices. (ex: “4”, “4.1”)|
Illustration of virtual capabilities is not over. There is something more. What if we wanted to override the behavior of a virtual capability for specific WURFL devices? This is possible thanks to the new control capabilities.
Control capabilities are a developer’s edge on virtual capabilities that do what you want, but not quite in certain cases. If a virtual capability called “foobar” exists, you can define a WURFL patch file with group ‘virtual’ and a capability ‘controlcap_foobar’ that the virtual capability will look up before it starts to compute a value. If the capability has value ‘default’ for that device, the virtual capability will perform the expected computations. Otherwise, it will return the value specified in the capability. In case of boolean capability, the token ‘force_true’ and ‘force_false’ should be used instead (“true” and “false” are reserved for truly boolean capabilities).
As an example, I’ll take inspiration from this blog post by Tim Kadlec that describes a bug in the support of the @-ms-viewport rule on Nokia Lumia 920 devices. This bug is particularly nasty if you want the device to render RWD sites. Assuming you care about this device not getting the RWD version of your site, the following patch would allow you to override the behavior of is_smartphone and treat the device as a non-smartphone:
<device id=”nokia_lumia_720_ver1″ user_agent=”Mozilla/5.0 (compatible; MSIE 10.0; Windows Phone 8.0; Trident/6.0; IEMobile/10.0; ARM; Touch; NOKIA; Lumia 720)” fall_back=”generic_ms_phone_os8″>
<capability name=”controlcap_is_smartphone” value=”force_false”/>
In addition to filters and virtual capabilities, API 1.5 comes with the usual batch of improvements in the detection logic. This is, of course, the main reason why people love WURFL and customers pay for updates. Going into details here is unnecessary. The following keywords will suffice: Firefox Mobile, Firefox OS, SmartTV, BlackBerry, Opera Mini, UCWeb, Bots and more…