iOS上是否有办法获取设备的用户代理?我不想硬编码,因为我需要所有设备的用户代理,我需要将用户代理附加到URL。
感谢。
答案 0 :(得分:39)
确定iOS中的用户代理的一种更简单的方法是使用this SO post的接受答案直接从UIWebView
获取。引用这个答案:
解决方案是创建一个UIWebView,然后使用javascript来提取用户代理。
UIWebView* webView = [[UIWebView alloc] initWithFrame:CGRectZero]; NSString* secretAgent = [webView stringByEvaluatingJavaScriptFromString:@"navigator.userAgent"];
答案 1 :(得分:4)
您实际上不需要发出请求以获取用户代理。只需从以下委托方法返回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;
}
答案 2 :(得分:4)
每个请求中的移动应用程序都必须发送其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>
如何获取每个组件?
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
有关设备的信息
modelName
-您可以获得described here
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
}
达尔文版本
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;版本13.3(内部版本17C45))CFNetwork / 1121.2.1 Darvin / 19.3.0
答案 3 :(得分:0)
UIWebView
已过时。在运行于iOS 8及更高版本的应用中,请使用WKWebView
类,而不要使用UIWebView
。
// WKWebView
{
WKWebView *webView = [[WKWebView alloc] initWithFrame:self.view.bounds];
[webView loadHTMLString:@"<html></html>" baseURL:nil];
[webView evaluateJavaScript:@"navigator.appName" completionHandler:^(id __nullable appName, NSError * __nullable error) {
NSLog(@"%@", appName);
// Netscape
}];
[webView evaluateJavaScript:@"navigator.userAgent" completionHandler:^(id __nullable userAgent, NSError * __nullable error) {
NSLog(@"%@", userAgent);
// iOS 8.3
// Mozilla/5.0 (iPhone; CPU iPhone OS 8_3 like Mac OS X) AppleWebKit/600.1.4 (KHTML, like Gecko) Mobile/12F70
// iOS 9.0
// Mozilla/5.0 (iPhone; CPU iPhone OS 9_0 like Mac OS X) AppleWebKit/601.1.32 (KHTML, like Gecko) Mobile/13A4254v
}];
// needs retain because `evaluateJavaScript:` is asynchronous
self.webView = webView;
}
答案 4 :(得分:0)
快速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")
}
}
}
答案 5 :(得分:0)
(iOS 8.0,*)
由于iOS 12中已弃用UIWebView,因此应改用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
的身份访问该属性
答案 6 :(得分:-1)
在iOS中确定用户代理的一种更简单的方法是使用this SO post接受的答案直接从UIWebView获取它。但这种方式有两个缺点:
1,UIWebView的首次分配可能需要花费太多时间来初始化webview上下文
2,代码必须在主线程中执行。这可能会妨碍主线
如果您知道如何使用私有方法的技巧,同时避免拒绝App Store Review
您可以尝试以下代码:
#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; }