Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,15 @@ final class OSLoggerPlatformProvider: ILoggerPlatformProvider {
static let deviceManufacturer = "Apple"
static let unknown = "unknown"
static let osBuildName = "kern.osversion"
static let xcodeVersionInfoKey = "DTXcode"
static let xcodeVersionAttribute = "xcode_version"
static let xcodeBuildInfoKey = "DTXcodeBuild"
static let xcodeBuildAttribute = "xcode_build"
/// Catalyst bundles declare their floor as `LSMinimumSystemVersion`; iOS uses
/// `MinimumOSVersion`. First match wins.
static let minimumOSVersionInfoKeys = ["MinimumOSVersion", "LSMinimumSystemVersion"]
static let minimumOSVersionAttribute = "minimum_os_version"
static let applePlatformAttribute = "apple_platform"
static let disabledLogLevel = "NONE"
static let crashDirectoryComponents = ["onesignal", "logger", "crashes"]

Expand Down Expand Up @@ -107,15 +116,17 @@ final class OSLoggerPlatformProvider: ILoggerPlatformProvider {
let osBuildId = OSLoggerPlatformProvider.systemValue(named: Constants.osBuildName)
let sdkWrapper = OneSignalWrapper.sdkType
let sdkWrapperVersion = OneSignalWrapper.sdkVersion
/// Nil on iOS: this describes the *host app's* Kotlin stack, and an iOS app is
/// not a Kotlin host. The shared module's own provenance rides on
/// `ossdk.kmp_version` instead.
let kotlinVersion: String? = nil

/// Nil on iOS: there is no runtime API for the Swift language version, and anything
/// derivable would just re-encode `xcode_version` less precisely.
let swiftVersion: String? = nil

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i will remove this swiftVersion in another pr

let additionalVersionAttributes: [String: String] = {
#if targetEnvironment(macCatalyst)
return ["apple_platform": "mac_catalyst"]
#else
return [:]
#endif
}()

let additionalVersionAttributes: [String: String] =
OSLoggerPlatformProvider.hostBuildAttributes()
var enabledFeatureFlags: [String] {
featureFlagsProvider()
}
Expand Down Expand Up @@ -189,6 +200,63 @@ final class OSLoggerPlatformProvider: ILoggerPlatformProvider {

let apiBaseUrl = OS_API_SERVER_URL.trimmingCharacters(in: CharacterSet(charactersIn: "/"))

/// Toolchain and deployment target of the running executable, read from its
/// `Info.plist`. Read at runtime rather than with `#if swift(...)` because we ship a
/// prebuilt XCFramework — a compile-time check would describe OneSignal's build
/// machine, identically for every customer.
///
/// `minimum_os_version` is what the host *commits to* supporting, which is the fact
/// that gates raising our own deployment target. The OS actually running is separate
/// (`os.name` / `os.version` / `os.build_id`) and routinely diverges from it.
static func hostBuildAttributes(
infoDictionary: [String: Any]? = Bundle.main.infoDictionary
) -> [String: String] {
var attributes: [String: String] = [:]
#if targetEnvironment(macCatalyst)
attributes[Constants.applePlatformAttribute] = "mac_catalyst"
#endif
if let xcodeVersion = hostXcodeVersion(infoDictionary: infoDictionary) {
attributes[Constants.xcodeVersionAttribute] = xcodeVersion
}
if let xcodeBuild = infoValue(Constants.xcodeBuildInfoKey, in: infoDictionary) {
attributes[Constants.xcodeBuildAttribute] = xcodeBuild
}
if let minimumOSVersion = Constants.minimumOSVersionInfoKeys
.lazy
.compactMap({ infoValue($0, in: infoDictionary) })
.first {
attributes[Constants.minimumOSVersionAttribute] = minimumOSVersion
}
return attributes
}

private static func infoValue(_ key: String, in infoDictionary: [String: Any]?) -> String? {
guard let value = infoDictionary?[key] as? String, !value.isEmpty else {
return nil
}
return value
}

static func hostXcodeVersion(
infoDictionary: [String: Any]? = Bundle.main.infoDictionary
) -> String? {
guard let raw = infoDictionary?[Constants.xcodeVersionInfoKey] as? String else {
return nil
}
return decodeXcodeVersion(raw)
}

/// `DTXcode` packs major/minor/patch into digits: `"2620"` is 26.2, `"0900"` is 9.0.
static func decodeXcodeVersion(_ raw: String) -> String? {
guard let packed = Int(raw.trimmingCharacters(in: .whitespaces)), packed > 0 else {
return nil
}
let major = packed / 100
let minor = (packed / 10) % 10
let patch = packed % 10
return patch == 0 ? "\(major).\(minor)" : "\(major).\(minor).\(patch)"
}

private static func systemValue(named name: String) -> String {
var size = 0
guard sysctlbyname(name, nil, &size, nil, 0) == 0, size > 0 else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -428,6 +428,67 @@ final class OSLoggerAdaptersTests: XCTestCase {
}
}

/// Host build metadata read from the app's `Info.plist`, kept separate from the
/// adapter tests above so neither class outgrows the lint limit.
final class OSLoggerHostBuildAttributesTests: XCTestCase {
func testDecodeXcodeVersionUnpacksDTXcodeDigits() {
XCTAssertEqual(OSLoggerPlatformProvider.decodeXcodeVersion("2620"), "26.2")
XCTAssertEqual(OSLoggerPlatformProvider.decodeXcodeVersion("1600"), "16.0")
XCTAssertEqual(OSLoggerPlatformProvider.decodeXcodeVersion("0900"), "9.0")
XCTAssertEqual(OSLoggerPlatformProvider.decodeXcodeVersion("1632"), "16.3.2")
XCTAssertNil(OSLoggerPlatformProvider.decodeXcodeVersion(""))
XCTAssertNil(OSLoggerPlatformProvider.decodeXcodeVersion("not-a-number"))
}

func testHostBuildAttributesMapsInfoPlistKeys() {
let attributes = OSLoggerPlatformProvider.hostBuildAttributes(infoDictionary: [
"DTXcode": "2620",
"DTXcodeBuild": "17C52",
"MinimumOSVersion": "12.0",
// Present in every built Info.plist but deliberately not emitted: each is
// either constant across all apps or derivable from the fields above.
"DTCompiler": "com.apple.compilers.llvm.clang.1_0",
"DTPlatformName": "iphonesimulator",
"DTPlatformVersion": "26.2",
"DTPlatformBuild": "23C53",
"DTSDKName": "iphonesimulator26.2",
"DTSDKBuild": "23C53"
])

XCTAssertEqual(attributes["xcode_version"], "26.2")
XCTAssertEqual(attributes["xcode_build"], "17C52")
// The host's declared deployment target, not the OS it happens to run on.
XCTAssertEqual(attributes["minimum_os_version"], "12.0")
XCTAssertEqual(attributes.count, 3)
}

func testHostBuildAttributesFallsBackToCatalystMinimumSystemVersion() {
let attributes = OSLoggerPlatformProvider.hostBuildAttributes(infoDictionary: [
"LSMinimumSystemVersion": "13.1"
])

XCTAssertEqual(attributes["minimum_os_version"], "13.1")
}

func testHostBuildAttributesPrefersMinimumOSVersionOverCatalystKey() {
let attributes = OSLoggerPlatformProvider.hostBuildAttributes(infoDictionary: [
"MinimumOSVersion": "12.0",
"LSMinimumSystemVersion": "13.1"
])

XCTAssertEqual(attributes["minimum_os_version"], "12.0")
}

func testHostBuildAttributesOmitsBlankAndMissingValues() {
let attributes = OSLoggerPlatformProvider.hostBuildAttributes(infoDictionary: [
"DTXcodeBuild": "",
"MinimumOSVersion": ""
])

XCTAssertTrue(attributes.isEmpty)
}
}

private final class LoggerAdapterListener: NSObject, OSLogListener {
var levels: [ONE_S_LOG_LEVEL] = []

Expand Down
Loading