David
David

Reputation: 14404

How to get the user agent on iOS?

Is there a way on iOS to get the user agent of the device? I don't want to hard code it since I need the user agent for all devices and I need to append the user agent to a URL.

Thanks.

Upvotes: 19

Views: 36647

Answers (7)

Cloy
Cloy

Reputation: 2181

Objective-C

WKWebView *webView = [WKWebView new];
res = [webView valueForKey:@"userAgent"];

Swift

let ua = WKWebView().value(forKey: "userAgent")

Dont forget to import WebKit 😂

Upvotes: 1

hbk
hbk

Reputation: 11184

Mobile application in every request must send his User-Agent header with build version and device information

User agent configuration info

So, user agent should be like:

User-Agent: <AppName>/version (<system-information>) <platform> (<platform-details>) <extensions>

For iOS:

User-Agent: <AppName/<version> (<iDevice platform>; <Apple model identifier>; iOS/<OS version>) CFNetwork/<version> Darwin/<version>

How to get each of the components?

  1. Headers Key - u can hardcode or use some constant values

  2. AppName and version - grab from Info.plist

     let infoPlist = try? PListFile<InfoPlist>()
     let appName = infoPlist.data.bundleName
     let version = infoPlist.data.versionNumber
     let build = infoPlist.data.buildNumber
    
  3. Info about Device

modelName - you can obtain like described here

    let modelName = UIDevice.current.modelName

other components:

    let platform = UIDevice.current.systemName
    let operationSystemVersion = ProcessInfo.processInfo.operatingSystemVersionString
  1. CFNetwork version

     static var cfNetworkVersion: String? {
       guard
         let bundle = Bundle(identifier: "com.apple.CFNetwork"),
          let versionAny = bundle.infoDictionary?[kCFBundleVersionKey as String],
          let version = versionAny as? String
           else { return nil }
       return version
     }
    

from here

  1. Darwin Version

      var systemInfo = utsname()
      uname(&systemInfo)
      let machineMirror = Mirror(reflecting: systemInfo.release)
      let darvinVersionString = machineMirror.children.reduce("") { identifier, element in
        guard let value = element.value as? Int8,
          value != 0 else {
            return identifier
        }
    
        return identifier + String(UnicodeScalar(UInt8(value)))
      }
    

from here

Result:

MyApp/1.8.199 (iOS; iPhone XS; Version 13.3 (Build 17C45)) CFNetwork/1121.2.1 Darvin/19.3.0

Upvotes: 14

Giuseppe Mazzilli
Giuseppe Mazzilli

Reputation: 490

(iOS 8.0, *)

Since UIWebView is deprecated in iOS 12, you should use WKWebView instead.

Since WKWebView.evaluateJavaScript(_:) result is now async, this implementation solve a common requirement to have userAgent available in your own REST api call.

import WebKit

class UAString {

    static var userAgent : String = ""

    @discardableResult init(view parent: UIView) {

        if UAString.userAgent.isEmpty {

            let webView = WKWebView(frame: .zero, configuration: WKWebViewConfiguration())

            webView.translatesAutoresizingMaskIntoConstraints = false
            parent.addSubview(webView)

            webView.evaluateJavaScript("navigator.userAgent") { result, _ in
                UAString.userAgent = result as? String ?? ""
            }
        }
    }

}

Now last part you can implement this class in your initial view controller as follow:

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)

    UAString(view: self.view)
}

then you can access the attribute as UAString.userAgent

Upvotes: 3

iAj
iAj

Reputation: 3817

Swift 3.x, 4.x, 5.x, & above

As sometime traditional UIWebView get's memory leaked so instead use always WKWebView (far better from UIWebView)

import WebKit

var webViewForUserAgent: WKWebView?

and get userAgent by calling below function & you can also set it to your other variable

func getUserAgent() {

    webViewForUserAgent = WKWebView() // must initialize

    webViewForUserAgent?.evaluateJavaScript("navigator.userAgent") { (result, error) in

        //
        if error != nil {
            print("Error occured to get userAgent")
            return
        }

        //
        if let unwrappedUserAgent = result as? String {
            print("userAgent: \(unwrappedUserAgent)")
        } else {
            print("Failed to get userAgent")
        }
    }
}

enter image description here

Upvotes: 0

Jim Green
Jim Green

Reputation: 16

A simpler way to ascertain the user agent in iOS is to get it directly from a UIWebView using the accepted answer to this SO post.But this way has two disadvantages:
1、UIWebView's first allocation may take too much time in initializing webview context.
2、the code must be executed in main thread. This may stuck main thread.
If you know the tricks of how to use private methods while avoiding the refusal of App Store Review.
You can try the following code:


    #define CALL_PRIVATE_INSTANCEMETHOD(x,sel,q)\
    {\
    SEL selector = NSSelectorFromString([NSString stringWithFormat:@"%@",@#sel]);\
    if ([x respondsToSelector:selector]) {\
    _Pragma("clang diagnostic push")\
    _Pragma("clang diagnostic ignored \"-Warc-performSelector-leaks\"")\
    q=[x performSelector:selector];\
    _Pragma("clang diagnostic pop")\
    }\
    }\

    #define CALL_PRIVATE_CLASSMETHOD_ONEPARAM(x,sel,p,q)\
    {\
    SEL selector = NSSelectorFromString([NSString stringWithFormat:@"_%@:",@#sel]);\
    if ([x respondsToSelector:selector]) {\
    _Pragma("clang diagnostic push")\
    _Pragma("clang diagnostic ignored \"-Warc-performSelector-leaks\"")\
    q=[x performSelector:selector withObject:p];\
    _Pragma("clang diagnostic pop")\
    }\
    }\

    + (NSString *)standardUserAgent{
        NSString *buildVersion = nil;
        CALL_PRIVATE_INSTANCEMETHOD([UIDevice currentDevice], buildVersion,buildVersion);

        Class webViewCls = NSClassFromString([NSString stringWithFormat:@"%@%@",@"Web",@"View"]);
        NSString *standardUA = nil;
        NSString *versions = [NSString stringWithFormat:@"Mobile/%@",buildVersion];
        CALL_PRIVATE_CLASSMETHOD_ONEPARAM(webViewCls, standardUserAgentWithApplicationName,versions,standardUA);    
        return standardUA;
    }

Upvotes: -1

aimless
aimless

Reputation: 635

You don’t actually need to make the request in order to get the user-agent. Just return NO from the following delegate method and retain the user-Agent header:

-(BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType

It might look something like this:

-(BOOL)webView:(UIWebView *)webView
 shouldStartLoadWithRequest:(NSURLRequest *)request
 navigationType:(UIWebViewNavigationType)navigationType
{  
     userAgent = [[request valueForHTTPHeaderField:@"User-Agent"] copy]; 
     NSLog(@"user-agent: %@", userAgent);

     _webView.delegate = nil; 
     [_webView release]; 

     return NO; 
}

Upvotes: 6

user577537
user577537

Reputation:

A simpler way to ascertain the user agent in iOS is to get it directly from a UIWebView using the accepted answer to this SO post. To quote that answer:

The solution was to create a UIWebView and then just use javascript to pull out the user agent.

UIWebView* webView = [[UIWebView alloc] initWithFrame:CGRectZero];
NSString* secretAgent = [webView stringByEvaluatingJavaScriptFromString:@"navigator.userAgent"];

Upvotes: 44

Related Questions