在iOS上,是否有一种方法可以获取设备的用户代理(User Agent)?我不想硬编码它,因为我需要所有设备的用户代理,并且我需要将用户代理添加到一个URL中。
谢谢。
在iOS中确定用户代理的更简单的方法是直接从UIWebView
获取它,使用这个SO文章中被接受的答案。引用那个答案:
解决方案是创建一个UIWebView,然后只需使用javascript提取用户代理即可。
UIWebView* webView = [[UIWebView alloc] initWithFrame:CGRectZero]; NSString* secretAgent = [webView stringByEvaluatingJavaScriptFromString:@"navigator.userAgent"];
每个请求的移动应用程序都必须发送带有构建版本和设备信息的User-Agent
头。
因此,用户代理应该是这样的:
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>
如何获取每个组件?
标头键 - 您可以硬编码或使用一些常量值
应用名称和版本 - 从Info.plist
中获取
let infoPlist = try? PListFile<InfoPlist>()
let appName = infoPlist.data.bundleName
let version = infoPlist.data.versionNumber
let build = infoPlist.data.buildNumber
关于设备的信息
modelName
- 您可以像这里描述的那样获得。
let modelName = UIDevice.current.modelName
其他组件:
let platform = UIDevice.current.systemName
let operationSystemVersion = ProcessInfo.processInfo.operatingSystemVersionString
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
}
Darwin 版本
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)))
}
结果:
MyApp/1.8.199 (iOS; iPhone XS; Version 13.3 (Build 17C45)) CFNetwork/1121.2.1 Darvin/19.3.0
您实际上不需要发起请求即可获取用户代理。只需从以下委托方法返回NO并保留用户代理标头:
-(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;
}
(iOS 8.0, *)
自从iOS 12中UIWebView被废弃,您应该改用WKWebView。
由于WKWebView.evaluateJavaScript(_:)结果现在是异步的,因此此实现解决了一个常见要求,在自己的REST API调用中需要用户代理。
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
。Objective-C
WKWebView *webView = [WKWebView new];
res = [webView valueForKey:@"userAgent"];
Swift
let ua = WKWebView().value(forKey: "userAgent")
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")
}
}
}
#define 调用私有实例方法(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 调用私有类方法带一个参数(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 *)标准用户代理{ NSString *buildVersion = nil; 调用私有实例方法([UIDevice currentDevice], buildVersion,buildVersion);
Class webViewCls = NSClassFromString([NSString stringWithFormat:@"%@%@",@"Web",@"View"]); NSString *standardUA = nil; NSString *versions = [NSString stringWithFormat:@"Mobile/%@",buildVersion]; 调用私有类方法带一个参数(webViewCls, standardUserAgentWithApplicationName,versions,standardUA); return standardUA; }
URLSession
的用户代理可能与用于UIWebView
的用户代理不同。 - mariusUIWebView
已被弃用,因此不应再使用此解决方案 :) - Axemasta