How to Correctly Form User-Agents for Mobile Apps

How to Correctly Form User-Agents for Mobile Apps

Native apps, Mobile apps, or just apps. We call them different things and we use them all the time. User-Agents for Mobile AppsFrom a business perspective, apps complement mobile and desktop sites. They are natural extension of an online strategy. Because of this, it’s important to take the necessary steps to make sure the native apps, and their versions, are identifiable. This is critical both from an analytics perspective as well as from a devops or support perspective. Native apps have much in common with the web. Apps often use the same online resources as the web site like RESTful APIs, images or videos. Because these resources are shared, it is very important to correctly form User-Agents for mobile apps to identify who is using the resources. Is the user on the Android app or iOS app? Which version of the app? Which OS version? And so on.

There are two kinds of mobile apps: 1) fully native apps, and 2) webview-based hybrid apps.  In this post, we will focus on fully native apps where HTTP requests are sent by the native code.

If you are interested in webview-based hybrid apps where a browser window is wrapped in a native shell, then check out this other post on User-Agent strings for hybrid webview apps.

The User-Agent

There is already an industry standard for device identification that apps can also use. It’s the User-Agent string. The User-Agent string is a field in the HTTP header that can identify the app that is making the request. In a web browsing experience, the User-Agent is set by the browser (which is also an app). Now, instead of a browser, our native app will be making the HTTP request. When we’re developing a native app for a mobile phone, we need to take steps to ensure our User-Agent includes information that is meaningful. Otherwise the app will get the default values, which is not very helpful in any context.

How to form a meaningful User-Agents for mobile apps

The specification is clear:

The “User-Agent” header field contains information about the user agent originating the request, which is often used by servers to help identify the scope of reported interoperability problems, to work around or tailor responses to avoid particular user agent limitations, and for analytics regarding browser or operating system use.

The specification goes in more detail and concludes that User-Agents should consist of one or more product identifiers and optional versions. By convention, the product identifiers are listed in decreasing order of their significance for identifying the User-Agent software.

Let’s dive right in and see how to compose meaningful User-Agent strings for native apps in the different platforms:

Native Android App

Based on the specification above, the template we want for the User-Agent of our app is:

<AppName>/<version> Dalvik/<version> (Linux; U; Android <android version>; <device ID> Build/<buildtag>)

For example:

Myapp/1 Dalvik/2.1.0 (Linux; U; Android 6.0.1; vivo 1610 Build/MMB29M)

The significant part here is “Myapp/1” which identifies the native app and the version of the app. This is the most significant. The rest of the string identifies the underlying software. In Android, it is relatively easy to compose a custom User-Agent string. Here is some sample code:

URLConnection cn = new URL("http://....").openConnection();
cn.setRequestProperty("User-agent", “Myapp/1 ” + System.getProperty("http.agent"));
cn.connect();

If you’re using the popular Picasso library, then the composition is similar. However, the actual request is made in a slightly different way. Have a look a this example app built with Picasso on Github.

Native iOS App

User-Agents for iOS are slightly more complex due to the fact that we have to account for both Objective-C and Swift. A default User-Agent in iOS may look something like this:

MyApp/1 CFNetwork/808.3 Darwin/16.3.0

In iOS a component called “CFNetwork” handles the network communications. Also involved is the UNIX version iOS is built on: “Darwin”. Both of these are mentioned in the default User-Agent. In addition there may be a custom app identifier added. So not very meaningful.

In order to construct a more meaningful User-Agent we need more information added to it, like so:

<AppName/<version> <iDevice platform><Apple model identifier>  iOS/<OS version> CFNetwork/<version> Darwin/<version>

For example:

MyApp/1 iPhone5,2 iOS/10_1 CFNetwork/808.3 Darwin/16.3.0

The main challenge in constructing the User-Agent in iOS is to collect all the parameters needed. For example, it is not obvious how to find the CFNetwork- and Darwin values. We’ve wrote an example app in Objective-C and Swift with the functionality needed to gather all bits that go into the User-Agent string. Feel free to use it. Once the bits are collected, just call the function to compose the User-Agent before you make a request:

Objective-C

NSString* userAgent = getUAString();
NSURL* url = [NSURL URLWithString:@""];
NSMutableURLRequest* request = [[NSMutableURLRequest alloc] initWithURL:url];
[request setValue:userAgent forHTTPHeaderField:@"User-Agent"];

For full code of the getUAString() function, see Github.

Swift

let userAgent = UAString()
if let url = NSURL(string: "") {
 let request = NSMutableURLRequest(url: url as URL)
 request.setValue(userAgent, forHTTPHeaderField: "User-Agent")
 //...
}

For full code of the getUAString() function, see Github.

Windows Phone

Even if not as popular as Android and iOS, Windows Phone is also worth mentioning. The semantic of the User-Agent is similar:

<app name>/<version> Windows Phone/<OS version> <device brand name>; <device model name>)

For example:

MyApp/1 Windows Phone/10.0 Microsoft; Lumia 550

You can get the device brand name and model name from the EasClientDeviceInformation class with the SystemManufacturer and SystemProductName properties. The OS version can be extracted from DeviceFamilyVersion property in the AnalyticsVersionInfo class.

Final Thoughts

Even if your favorite library or framework is not covered in this post, these examples should be generic enough to be transferred to any framework. It’s worth spending the additional few minutes of development to make sure the User-Agent of your app is meaningful and conforms to specification. Your analytics, devops, and debugging will all benefit. And likewise, you can leverage this device detection to  target specific devices, apps, or app versions on specific platforms with messages, ads or content.

This article covered User-Agents in fully native apps. If your app is a webview based app (or hybrid app), then read “Correctly Form User-Agents for Webview Apps”