【问题标题】:How to get the user agent on iOS?如何在 iOS 上获取用户代理?
【发布时间】:2012-01-24 15:03:25
【问题描述】:

iOS 上有没有办法获取设备的用户代理?我不想硬编码,因为我需要所有设备的用户代理,并且我需要将用户代理附加到 URL。

谢谢。

【问题讨论】:

    标签: iphone ios


    【解决方案1】:

    每个请求中的移动应用程序必须发送他的User-Agent 标头以及构建版本和设备信息

    User agent configuration info

    所以,用户代理应该是这样的:

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

    对于 iOS:

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

    如何获取各个组件?

    1. Headers Key - 你可以硬编码或使用some constant values

    2. AppName 和版本 - 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. 关于设备的信息

    modelName - 你可以像described here 一样获得

        let modelName = UIDevice.current.modelName
    

    其他组件:

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

       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. 达尔文版

        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

    结果:

    MyApp/1.8.199(iOS;iPhone XS;版本 13.3(内部版本 17C45))CFNetwork/1121.2.1 Darvin/19.3.0

    【讨论】:

      【解决方案2】:

      (iOS 8.0,*)

      由于 UIWebView 在 iOS 12 中已弃用,您应该改用 WKWebView。

      由于 WKWebView.evaluateJavaScript(_:) 结果现在是异步的,因此此实现解决了在您自己的 REST api 调用中提供 userAgent 的常见要求。

      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 ?? ""
                  }
              }
          }
      
      }
      

      现在最后一部分你可以在你的初始视图控制器中实现这个类,如下所示:

      override func viewWillAppear(_ animated: Bool) {
          super.viewWillAppear(animated)
      
          UAString(view: self.view)
      }
      

      那么您可以使用UAString.userAgent 访问该属性

      【讨论】:

        【解决方案3】:

        Swift 3.x、4.x、5.x 及更高版本

        由于有时传统的 UIWebView 会发生内存泄漏,因此请始终使用 WKWebView(比 UIWebView 好得多)

        import WebKit
        
        var webViewForUserAgent: WKWebView?
        

        并通过调用下面的函数来获取 userAgent,您也可以将其设置为您的其他变量

        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")
                }
            }
        }
        

        【讨论】:

        • 这不会编译,因为 WKWebView() 需要参数。
        • 请分享您的代码 sn-p,确保您将 webView 声明为可选
        【解决方案4】:

        在 iOS 中确定用户代理的更简单方法是使用已接受的this SO post 答案直接从 UIWebView 获取它。但这种方法有两个缺点:
        1、UIWebView的第一次分配可能会在初始化webview上下文时花费太多时间。
        2、代码必须在主线程中执行。这可能会卡住主线程。
        如果您知道如何使用私有方法同时避免 App Store Review 拒绝的技巧。
        你可以试试下面的代码:

        #define CALL_PRIVATE_INSTANCEMETHOD(x,sel,q)\ {\ SEL 选择器 = NSSelectorFromString([NSString stringWithFormat:@"%@",@#sel]);\ if ([x respondsToSelector:selector]) {\ _Pragma("clang 诊断推送")\ _Pragma("clang 诊断被忽略\"-Warc-performSelector-leaks\"")\ q=[x performSelector:selector];\ _Pragma("clang 诊断弹出")\ }\ }\ #define CALL_PRIVATE_CLASSMETHOD_ONEPARAM(x,sel,p,q)\ {\ SEL 选择器 = NSSelectorFromString([NSString stringWithFormat:@"_%@:",@#sel]);\ if ([x respondsToSelector:selector]) {\ _Pragma("clang 诊断推送")\ _Pragma("clang 诊断被忽略\"-Warc-performSelector-leaks\"")\ q=[x performSelector:selector withObject:p];\ _Pragma("clang 诊断弹出")\ }\ }\ + (NSString *)standardUserAgent{ NSString *buildVersion = nil; CALL_PRIVATE_INSTANCEMETHOD([UIDevice currentDevice], buildVersion,buildVersion); 类 webViewCls = NSClassFromString([NSString stringWithFormat:@"%@%@",@"Web",@"View"]); NSString *standardUA = nil; NSString *versions = [NSString stringWithFormat:@"Mobile/%@",buildVersion]; CALL_PRIVATE_CLASSMETHOD_ONEPARAM(webViewCls,standardUserAgentWithApplicationName,versions,standardUA); 返回标准UA; }

        【讨论】:

          【解决方案5】:

          您实际上不需要发出请求来获取用户代理。只需从以下委托方法返回 NO 并保留 user-Agent 标头:

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

          它可能看起来像这样:

          -(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; 
          }
          

          【讨论】:

            【解决方案6】:

            在 iOS 中确定用户代理的一种更简单的方法是使用接受的this SO post 答案直接从UIWebView 获取它。引用这个答案:

            解决方案是创建一个 UIWebView,然后使用 javascript 拉出用户代理。

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

            【讨论】:

            • 无需发出 Web 请求并等待其实现以获取 http 标头。对我来说这就是答案!谢谢
            • 谢谢你,它有效。一个重要的注意事项是,您需要在主线程中调用它。因此,在某些情况下,您可能需要将答案代码包装在 dispatch_async(dispatch_get_main_queue(), ^{ ... });
            • UIWebview 不复存在
            • 这并不完全准确,用于URLSession 的用户代理可能与用于UIWebView 的用户代理不同。
            猜你喜欢
            • 2014-07-17
            • 2011-03-18
            • 2012-05-01
            • 1970-01-01
            • 1970-01-01
            • 2012-04-10
            • 2018-11-20
            • 1970-01-01
            相关资源
            最近更新 更多