Sources/Confidence/ConfidenceDeviceInfoContextDecorator.swift (69 lines of code) (raw):

#if os(iOS) || os(tvOS) || os(visionOS) || targetEnvironment(macCatalyst) import Foundation import UIKit import Combine /** Helper class to produce device information context for the Confidence context. The values appended to the Context come primarily from the Bundle or UiDevice API AppInfo contains: - version: the version name of the app. - build: the version code of the app. - namespace: the package name of the app. DeviceInfo contains: - manufacturer: the manufacturer of the device. - brand: the brand of the device. - model: the model of the device. - type: the type of the device. OsInfo contains: - name: the name of the OS. - version: the version of the OS. Locale contains: - locale: the locale of the device. - preferred_languages: the preferred languages of the device. The context is only updated when the class is initialized and then static. */ public class ConfidenceDeviceInfoContextDecorator { private let staticContext: ConfidenceValue public init( withDeviceInfo: Bool = false, withAppInfo: Bool = false, withOsInfo: Bool = false, withLocale: Bool = false ) { var context: [String: ConfidenceValue] = [:] if withDeviceInfo { let device = UIDevice.current context["device"] = .init(structure: [ "manufacturer": .init(string: "Apple"), "model": .init(string: Self.getDeviceModelIdentifier()), "type": .init(string: device.model) ]) } if withAppInfo { let currentVersion: String = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? "" let currentBuild: String = Bundle.main.infoDictionary?["CFBundleVersion"] as? String ?? "" let bundleId = Bundle.main.bundleIdentifier ?? "" context["app"] = .init(structure: [ "version": .init(string: currentVersion), "build": .init(string: currentBuild), "namespace": .init(string: bundleId) ]) } if withOsInfo { let device = UIDevice.current context["os"] = .init(structure: [ "name": .init(string: device.systemName), "version": .init(string: device.systemVersion) ]) } if withLocale { let locale = Locale.current let preferredLanguages = Locale.preferredLanguages // Top level fields context["locale"] = .init(string: locale.identifier) // Locale identifier (e.g., "en_US") context["preferred_languages"] = .init(list: preferredLanguages.map { lang in .init(string: lang) }) } self.staticContext = .init(structure: context) } /** Returns a context where values are decorated (appended) according to how the ConfidenceDeviceInfoContextDecorator was setup. The context values in the parameter context have precedence over the fields appended by this class. */ public func decorated(context contextToDecorate: [String: ConfidenceValue]) -> [String: ConfidenceValue] { var result = self.staticContext.asStructure() ?? [:] contextToDecorate.forEach { (key: String, value: ConfidenceValue) in result[key] = value } return result } private static func getDeviceModelIdentifier() -> String { var systemInfo = utsname() uname(&systemInfo) let machineMirror = Mirror(reflecting: systemInfo.machine) let identifier = machineMirror.children .compactMap { element in element.value as? Int8 } .filter { $0 != 0 } .map { Character(UnicodeScalar(UInt8($0))) } return String(identifier) } } #endif