Files
MatchLiveTv/native/ios/MatchLiveTv/Core/DeviceTelemetry.swift
T

96 lines
3.2 KiB
Swift

import Foundation
import UIKit
import Network
import CoreTelephony
struct DeviceHealth: Sendable {
let batteryPercent: Int
let thermalState: ThermalState
let networkType: String
}
struct ClientInfoPayload: Encodable, Sendable {
let os: String
let appVersion: String?
let appBuild: String?
let deviceManufacturer: String?
let deviceModel: String?
let osVersion: String?
let carrier: String?
}
enum DeviceTelemetry {
static func snapshot(thermalState: ThermalState? = nil) -> DeviceHealth {
UIDevice.current.isBatteryMonitoringEnabled = true
let level = Int((UIDevice.current.batteryLevel >= 0 ? UIDevice.current.batteryLevel : 1) * 100)
let thermal = thermalState ?? ThermalState.from(ProcessInfo.processInfo.thermalState)
return DeviceHealth(
batteryPercent: level,
thermalState: thermal,
networkType: currentNetworkType()
)
}
static func clientInfo() -> ClientInfoPayload {
let bundle = Bundle.main
let version = bundle.object(forInfoDictionaryKey: "CFBundleShortVersionString") as? String
let build = bundle.object(forInfoDictionaryKey: "CFBundleVersion") as? String
let model = UIDevice.current.model
// Prefer machine identifier when available (e.g. iPhone15,2)
let machine = utsnameMachine()
return ClientInfoPayload(
os: "ios",
appVersion: version,
appBuild: build,
deviceManufacturer: "Apple",
deviceModel: machine ?? model,
osVersion: UIDevice.current.systemVersion,
carrier: carrierName()
)
}
private static func currentNetworkType() -> String {
let monitor = NWPathMonitor()
let semaphore = DispatchSemaphore(value: 0)
var result = L10n.t("network.type.unknown")
monitor.pathUpdateHandler = { path in
if path.usesInterfaceType(.wifi) {
result = L10n.t("network.type.wifi")
} else if path.usesInterfaceType(.cellular) {
result = L10n.t("network.type.cellular")
} else if path.usesInterfaceType(.wiredEthernet) {
result = L10n.t("network.type.ethernet")
}
semaphore.signal()
}
let queue = DispatchQueue(label: "network.type")
monitor.start(queue: queue)
_ = semaphore.wait(timeout: .now() + 0.5)
monitor.cancel()
return result
}
private static func carrierName() -> String? {
let info = CTTelephonyNetworkInfo()
if let providers = info.serviceSubscriberCellularProviders {
for carrier in providers.values {
if let name = carrier.carrierName?.trimmingCharacters(in: .whitespacesAndNewlines),
!name.isEmpty {
return name
}
}
}
return nil
}
private static func utsnameMachine() -> String? {
var systemInfo = utsname()
uname(&systemInfo)
return withUnsafePointer(to: &systemInfo.machine) {
$0.withMemoryRebound(to: CChar.self, capacity: 1) {
String(validatingUTF8: $0)
}
}
}
}