diff --git a/Sources/Gravatar/BundleInfo.swift b/Sources/Gravatar/BundleInfo.swift index 8d2dbe70..674065db 100644 --- a/Sources/Gravatar/BundleInfo.swift +++ b/Sources/Gravatar/BundleInfo.swift @@ -5,10 +5,23 @@ package enum BundleInfo { getInfoValue(forKey: "CFBundleShortVersionString") as? String } + /// The `CFBundleName`. + /// + /// This string may be localized. package static var appName: String? { Bundle.main.object(forInfoDictionaryKey: "CFBundleName") as? String } + /// The `CFBundleExecutable` name. + package static var executableName: String? { + Bundle.main.object(forInfoDictionaryKey: "CFBundleExecutable") as? String + } + + package static var appIdentifier: String? { + guard let bundleID = Bundle.main.bundleIdentifier else { return nil } + return String(bundleID.hashed().prefix(10)) + } + private static func getInfoValue(forKey key: String) -> Any? { // Access the SDKInfo.plist using Bundle.module guard let url = Bundle.module.url(forResource: "SDKInfo", withExtension: "plist"), diff --git a/Sources/Gravatar/Network/Services/URLSessionHTTPClient.swift b/Sources/Gravatar/Network/Services/URLSessionHTTPClient.swift index 29a01482..d960a88a 100644 --- a/Sources/Gravatar/Network/Services/URLSessionHTTPClient.swift +++ b/Sources/Gravatar/Network/Services/URLSessionHTTPClient.swift @@ -7,6 +7,10 @@ enum HTTPClientError: Error { case URLSessionError(Error) } +private enum Constants { + static let sdkName = "Gravatar-SDK" +} + struct URLSessionHTTPClient: HTTPClient { private let urlSession: URLSessionProtocol @@ -14,9 +18,10 @@ struct URLSessionHTTPClient: HTTPClient { let configuration = URLSessionConfiguration.default configuration.httpAdditionalHeaders = [ "Accept": "application/json", - "X-Platform": "ios", + "X-Platform": osName().lowercased(), "X-SDK-Version": BundleInfo.sdkVersion ?? "", - "X-Source": BundleInfo.appName ?? "", + "X-Source": BundleInfo.executableName ?? "", + "User-Agent": Self.userAgent, ] self.urlSession = urlSession ?? URLSession(configuration: configuration) } @@ -51,6 +56,23 @@ extension URLRequest { } } +extension URLSessionHTTPClient { + private static var userAgent: String { + "\(Constants.sdkName)/\(BundleInfo.sdkVersion) (\(osName()) \(ProcessInfo.processInfo.osVersionDottedString); AppID \(BundleInfo.appIdentifier ?? "Unknown"))" + } +} + +private func osName() -> String { + let osName: String + #if os(iOS) + osName = "iOS" + #else + osName = "Unknown OS" + assertionFailure("Update '\(#function)' to include the current OS name (iOS, macOS, tvOS, watchOS) when adding support for it.") + #endif + return osName +} + private func validatedHTTPResponse(_ response: URLResponse, data: Data) throws -> HTTPURLResponse { guard let httpResponse = response as? HTTPURLResponse else { throw HTTPClientError.invalidURLResponseError(response) diff --git a/Sources/Gravatar/ProcessInfo+Additions.swift b/Sources/Gravatar/ProcessInfo+Additions.swift new file mode 100644 index 00000000..22d91bf4 --- /dev/null +++ b/Sources/Gravatar/ProcessInfo+Additions.swift @@ -0,0 +1,7 @@ +import Foundation + +extension ProcessInfo { + var osVersionDottedString: String { + "\(operatingSystemVersion.majorVersion).\(operatingSystemVersion.minorVersion).\(operatingSystemVersion.patchVersion)" + } +}