-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
refactor!: restructure automatic context decorator (#185)
RELEASE-AS: 1.3.0
- Loading branch information
Showing
6 changed files
with
190 additions
and
118 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
105 changes: 0 additions & 105 deletions
105
Sources/Confidence/ConfidenceAppLifecycleProducer.swift
This file was deleted.
Oops, something went wrong.
114 changes: 114 additions & 0 deletions
114
Sources/Confidence/ConfidenceDeviceInfoContextDecorator.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
#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 |
34 changes: 34 additions & 0 deletions
34
Tests/ConfidenceTests/ConfidenceDeviceInfoContextDecoratorTests.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
import XCTest | ||
@testable import Confidence | ||
|
||
final class DeviceInfoContextDecoratorTests: XCTestCase { | ||
func testEmptyConstructMakesNoOp() { | ||
let result = ConfidenceDeviceInfoContextDecorator().decorated(context: [:]) | ||
XCTAssertEqual(result.count, 0) | ||
} | ||
|
||
func testAddDeviceInfo() { | ||
let result = ConfidenceDeviceInfoContextDecorator(withDeviceInfo: true).decorated(context: [:]) | ||
XCTAssertEqual(result.count, 1) | ||
XCTAssertNotNil(result["device"]) | ||
XCTAssertNotNil(result["device"]?.asStructure()?["model"]) | ||
XCTAssertNotNil(result["device"]?.asStructure()?["type"]) | ||
XCTAssertNotNil(result["device"]?.asStructure()?["manufacturer"]) | ||
} | ||
|
||
func testAddLocale() { | ||
let result = ConfidenceDeviceInfoContextDecorator(withLocale: true).decorated(context: [:]) | ||
XCTAssertEqual(result.count, 2) | ||
XCTAssertNotNil(result["locale"]) | ||
XCTAssertNotNil(result["preferred_languages"]) | ||
} | ||
|
||
func testAppendsData() { | ||
let result = ConfidenceDeviceInfoContextDecorator( | ||
withDeviceInfo: true | ||
).decorated(context: ["my_key": .init(double: 42.0)]) | ||
XCTAssertEqual(result.count, 2) | ||
XCTAssertEqual(result["my_key"]?.asDouble(), 42.0) | ||
XCTAssertNotNil(result["device"]) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters