diff --git a/Sources/ATProtoKit/Utilities/APIClientService.swift b/Sources/ATProtoKit/Utilities/APIClientService.swift index 9bb0d6ec83..7f28b9f63b 100644 --- a/Sources/ATProtoKit/Utilities/APIClientService.swift +++ b/Sources/ATProtoKit/Utilities/APIClientService.swift @@ -19,17 +19,74 @@ public actor APIClientService { /// The `URLSession` instance to be used for network requests. private(set) var urlSession: URLSession + + /// The `UserAgent` instance to identify all network requests originating from the `ATProtoKit` sdk + public static let userAgent: String = { + let info = Bundle.main.infoDictionary + let executable = (info?["CFBundleExecutable"] as? String) ?? + (ProcessInfo.processInfo.arguments.first?.split(separator: "/").last.map(String.init)) ?? + "Unknown" + let bundle = info?["CFBundleIdentifier"] as? String ?? "Unknown" + let appVersion = info?["CFBundleShortVersionString"] as? String ?? "Unknown" + let appBuild = info?["CFBundleVersion"] as? String ?? "Unknown" + + let osNameVersion: String = { + let version = ProcessInfo.processInfo.operatingSystemVersion + let versionString = "\(version.majorVersion).\(version.minorVersion).\(version.patchVersion)" + let osName: String = { + #if os(iOS) + #if targetEnvironment(macCatalyst) + return "macOS(Catalyst)" + #else + return "iOS" + #endif + #elseif os(watchOS) + return "watchOS" + #elseif os(tvOS) + return "tvOS" + #elseif os(macOS) + #if targetEnvironment(macCatalyst) + return "macOS(Catalyst)" + #else + return "macOS" + #endif + #elseif swift(>=5.9.2) && os(visionOS) + return "visionOS" + #elseif os(Linux) + return "Linux" + #elseif os(Windows) + return "Windows" + #elseif os(Android) + return "Android" + #else + return "Unknown" + #endif + }() + + return "\(osName) \(versionString)" + }() + + /// This needs to be updated manually or read from a central location + /// To get truly accurate version would need to read from Package.resolved and I haven't found a way to do so + let atProtoVersion = "ATProtoKit/0.21.0" + + let userAgent = "\(executable)/\(appVersion) (\(bundle); build:\(appBuild); \(osNameVersion)) \(atProtoVersion)" + + return userAgent + }() /// A `URLSession` object for use in all HTTP requests. public static let shared = APIClientService() /// Creates an instance for use in accepting and returning API requests and /// responses respectively. - /// + /// /// - Parameter configuration: An instance of `URLSessionConfiguration`. /// Defaults to `.default`. private init() { - self.urlSession = URLSession(configuration: .default) + let sessionConfig = URLSessionConfiguration.default + sessionConfig.httpAdditionalHeaders = ["User-Agent": APIClientService.userAgent] + self.urlSession = URLSession(configuration: sessionConfig) } /// Configures the singleton instance with a custom `URLSessionConfiguration`.