User-Agent for iOS
Network User-Agent Estimated reading time: 4 minutesWhen we meet some person, we always want to introduce ourselves from the best side. We can describe ourselves by telling the name and a few interesting facts.
In the computer world, if we behave as a user - we also should introduce ourselves. One of the options is to use User-Agent
.
User-Agent
The User-Agent request header is a character string that lets servers and network peers identify the application, operating system, vendor, and/or version of the requesting user agent. source.
Mobile application in every request must send his User-Agent
in the header with build version and device information.
format:
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>
I’m mostly working with iOS, so this tutorial is dedicated to this OS.
So, the components are:
- Headers Key
- AppName and version
- Info about Device
- CFNetwork version
- Darwin Version
Headers Key
HTTP headers let the client and the server pass additional information with an HTTP request or response. An HTTP header consists of its case-insensitive name followed by a colon (:), then by its value. Whitespace before the value is ignored.
User-Agent
:
<Value>
The list of the header key can be found for example here
AppName and Version
This information available in the u’r Info.plist
file.
U can use the approach described here with InfoPlist
struct like below:
import Foundation
// MARK: - URLScheme
public typealias URLScheme = String
// MARK: - URLType
public struct URLType: Codable {
public private (set) var role: String?
public private (set) var iconFile: String?
public private (set) var urlSchemes: [String]
// MARK: - Codable
private enum Key: String, CodingKey {
case role = "CFBundleTypeRole"
case iconFile = "CFBundleURLIconFile"
case urlSchemes = "CFBundleURLSchemes"
}
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: URLType.Key.self)
role = try container.decodeIfPresent(String.self, forKey: .role)
iconFile = try container.decodeIfPresent(String.self, forKey: .iconFile)
urlSchemes = try container.decode([String].self, forKey: .urlSchemes)
}
}
// MARK: - InfoPlist
public struct InfoPlist: Codable {
public private (set) var displayName: String?
public private (set) var bundleId: String
public private (set) var bundleName: String?
public private (set) var versionNumber: String?
public private (set) var buildNumber: String?
public private (set) var urlTypes: [URLType]?
// MARK: - Codable
private enum Key: String, CodingKey {
case displayName = "CFBundleDisplayName"
case bundleName = "CFBundleName"
case bundleId = "CFBundleIdentifier"
case versionNumber = "CFBundleShortVersionString"
case buildNumber = "CFBundleVersion"
case urlTypes = "CFBundleURLTypes"
}
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: InfoPlist.Key.self)
bundleId = try container.decode(String.self, forKey: .bundleId)
versionNumber = try container.decode(String.self, forKey: .versionNumber)
buildNumber = try container.decode(String.self, forKey: .buildNumber)
displayName = try? container.decodeIfPresent(String.self, forKey: .displayName)
bundleName = try? container.decodeIfPresent(String.self, forKey: .bundleName)
urlTypes = try? container.decodeIfPresent([URLType].self, forKey: .urlTypes)
}
}
Result - u can access all information within a few lines of code:
let infoPlist = try? PListFile<InfoPlist>()
let appName = infoPlist.data.bundleName
let version = infoPlist.data.versionNumber
let build = infoPlist.data.buildNumber
Info about Device
In general, u should collect few components. A lot of solutions are available for this purpose.
To get modelName - use for example this source.
let modelName = UIDevice.current.modelName
To get platform and operation system:
let platform = UIDevice.current.systemName
let operationSystemVersion = ProcessInfo.processInfo.operatingSystemVersionString
CFNetwork version
This is a framework, that uses for accessing network services and handling changes in network configurations. Build on abstractions of network protocols to simplify tasks such as working with BSD sockets, administering HTTP and FTP servers, and managing Bonjour services. Read more.
To get info about the version of 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
}
here is the source of this code
Darwin Version
How to get the Darwin Version described here, and the code:
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)))
}
Combining all together
Now, the simplest part - combine all components:
static var userAgentHeader: [AnyHashable: Any] {
var customHeaders: [AnyHashable: Any] = [: ]
if let infoPlist = try? PListFile<InfoPlist>(),
let appName = infoPlist.data.bundleName,
let version = infoPlist.data.versionNumber,
let build = infoPlist.data.buildNumber,
let cfNetworkVersionString = ProcessInfo.cfNetworkVersion {
let modelName = UIDevice.current.modelName
let platform = UIDevice.current.systemName
let operationSystemVersion = ProcessInfo.processInfo.operatingSystemVersionString
let darwinVersionString = ProcessInfo.darwinVersion
let userAgentString = "\(appName)\(String.slash)\(version).\(build) " +
"(\(platform); \(modelName); \(operationSystemVersion)) " +
"CFNetwork/\(cfNetworkVersionString) " +
"Darwin/\(darwinVersionString)"
customHeaders[HTTPHeader.Key.userAgent] = userAgentString
}
return customHeaders
}
Result:
MyApp/1.1.1233 (iOS; iPhone XS; Version 13.3 (Build 17C45)) CFNetwork/1121.2.1 Darvin/19.3.0
Resources
Share on: