【发布时间】:2012-01-24 15:03:25
【问题描述】:
iOS 上有没有办法获取设备的用户代理?我不想硬编码,因为我需要所有设备的用户代理,并且我需要将用户代理附加到 URL。
谢谢。
【问题讨论】:
iOS 上有没有办法获取设备的用户代理?我不想硬编码,因为我需要所有设备的用户代理,并且我需要将用户代理附加到 URL。
谢谢。
【问题讨论】:
每个请求中的移动应用程序必须发送他的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>
如何获取各个组件?
Headers Key - 你可以硬编码或使用some constant values
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
【讨论】:
(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 访问该属性
【讨论】:
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")
}
}
}
【讨论】:
在 iOS 中确定用户代理的更简单方法是使用已接受的this SO post 答案直接从 UIWebView 获取它。但这种方法有两个缺点:
1、UIWebView的第一次分配可能会在初始化webview上下文时花费太多时间。
2、代码必须在主线程中执行。这可能会卡住主线程。
如果您知道如何使用私有方法同时避免 App Store Review 拒绝的技巧。
你可以试试下面的代码:
【讨论】:
您实际上不需要发出请求来获取用户代理。只需从以下委托方法返回 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;
}
【讨论】:
在 iOS 中确定用户代理的一种更简单的方法是使用接受的this SO post 答案直接从UIWebView 获取它。引用这个答案:
解决方案是创建一个 UIWebView,然后使用 javascript 拉出用户代理。
UIWebView* webView = [[UIWebView alloc] initWithFrame:CGRectZero]; NSString* secretAgent = [webView stringByEvaluatingJavaScriptFromString:@"navigator.userAgent"];
【讨论】:
URLSession 的用户代理可能与用于UIWebView 的用户代理不同。