ImageEngine Is not limited to serving content for Webpages, and is equally capable of serving content for Native Apps.
As ImageEngine relies heavily upon device detection, it is important that applications (fully native or hybrids) provide meaningful User-Agent strings in their HTTP requests. For hybrid apps this is less of a challenge, but for fully native apps this issue must be addressed in order to avoid the default values.
The User-Agent 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 into 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.
See these blog posts for more background:
Let’s dive right in and see how to compose meaningful User-Agent strings for native apps in the different platforms:
Native Apps
We distinguish between fully native apps and apps based on a “web-view” or browser component. Web view-based apps, sometimes called “hybrid apps”, display web content inside the app.
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 at 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 written 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 the 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 the full code of the getUAString() function, see Github.
Webview based Apps
How should the User-Agent be formed? In the following, we’ll demonstrate how to compose the User-Agent for webview based applications. The base of the User-Agent is the default User-Agent as provided by the platform's SDK, and then app and device information is appended. This is the defacto standard as it has developed in the wild:
<Stock User-Agent> WebViewApp <AppName>/<Version>
Note the WebViewApp
identifier before the app name and version.
Let’s look at how to compose the correct User-Agent for different platforms and operating systems. For this purpose, we’ll construct User-Agents for a webview app called 'Foo, version 1.
Hybrid Android apps
For our app running on a Google Pixel, this is how our User-Agent could look:
Mozilla/5.0 (Linux; Android 9; Pixel Build/PQ3A.190801.002; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/77.0.3865.73 Mobile Safari/537.36 WebViewApp Foo/1
The code to construct the User-Agent is:
String ua = new WebView(this).getSettings().getUserAgentString();
webviewToUse.getSettings().setUserAgentString(ua + " WebViewApp Foo/1");
Hybrid iOS Apps
For iOS it is slightly more complicated, but still not difficult. We still want to append the app name and version, but in addition, we want to append the specific iPhone model at the end:
Mozilla/5.0 (iPhone; CPU iPhone OS 12_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 WebViewApp Foo/1 iPhone11,8
Above is the User-Agent of Foo version 1 running on an iPhone iPhone XR (iPhone11,8).
The examples below use the deviceName()
function to get the model information. See Github for full code examples.
Objective C
In Objective C, we retrieve the user-agent by creating a webview:
UIWebView* webView = [[UIWebView alloc] initWithFrame:CGRectZero];
NSString* ua = [webView stringByEvaluatingJavaScriptFromString:@"navigator.userAgent"];
Then we modify the extracted User-Agent via the AppDelegate
file:
NSDictionary *dictionary = [NSDictionary dictionaryWithObjectsAndKeys: ua + @" WebViewApp Foo/1 " + [self deviceName], @"UserAgent", nil];
[[NSUserDefaults standardUserDefaults] registerDefaults:dictionary];
Swift
In Swift we do it like this:
let userAgent = UIWebView().stringByEvaluatingJavaScript(from: "navigator.userAgent")! + "WebViewApp Foo/1 " + getName();
UserDefaults.standard.register(defaults: ["UserAgent" : userAgent])
Comments
0 comments
Please sign in to leave a comment.