import SwiftUI import SFSafeSymbols private let df: DateFormatter = { let df = DateFormatter() df.locale = .current df.dateStyle = .short df.timeStyle = .short return df }() struct DeviceInfoView: View { private let storageWarnBytes = 500 let info: DeviceInfo @Binding var isPresented: Bool private var runTimeString: String { let number = info.time.secondsSincePowerOn guard number >= 60 else { return "\(number) s" } let minutes = number / 60 guard minutes > 1 else { return "1 min" } guard minutes >= 60 else { return "\(minutes) min" } let hours = minutes / 60 guard hours > 1 else { return "1 hour" } guard hours >= 60 else { return "\(hours) hours" } let days = hours / 24 guard days > 1 else { return "1 day" } return "\(days) days" } private var nextUpdateText: String { let secs = -info.time.nextMeasurement.secondsToNow guard secs > 1 else { return "Now" } return "In \(secs) s" } private var storageIcon: SFSymbol { if info.storageSize - info.numberOfRecordedBytes < storageWarnBytes { return .externaldriveTrianglebadgeExclamationmark } return .externaldrive } private var storageText: String { if info.storageSize <= 0 { return "\(info.numberOfRecordedBytes)" } return "\(info.numberOfRecordedBytes) / \(info.storageSize) Bytes (\(info.storageFillPercentage) %)" } func sensorView(_ sensor: TemperatureSensor?, id: Int) -> some View { VStack(alignment: .leading, spacing: 5) { Text("Sensor \(id)") .font(.headline) if let sensor { IconAndTextView( icon: sensor.temperatureIcon, text: "\(sensor.temperatureText) (\(sensor.updateText))") IconAndTextView( icon: .tag, text: sensor.hexAddress) } else { IconAndTextView( icon: .thermometerMediumSlash, text: "Not connected") } } } var updateText: String { guard info.time.date.secondsToNow > 3 else { return "Updated Now" } return "Updated \(info.time.date.timePassedText)" } var body: some View { NavigationView { VStack(alignment: .leading, spacing: 5) { VStack(alignment: .leading, spacing: 5) { Text("System") .font(.headline) IconAndTextView( icon: .power, text: "\(df.string(from: info.time.deviceStartTime)) (\(runTimeString))") IconAndTextView( icon: .touchid, text: "\(info.uniqueIdOfPowerCycle)") IconAndTextView( icon: .autostartstop, text: "Wakeup: \(info.wakeupReason.text)") } VStack(alignment: .leading, spacing: 5) { Text("Recording") .font(.headline) IconAndTextView( icon: .stopwatch, text: "\(nextUpdateText) (Every \(info.measurementInterval) s)") } VStack(alignment: .leading, spacing: 5) { Text("Storage") .font(.headline) IconAndTextView( icon: .speedometer, text: "\(info.numberOfStoredMeasurements) Measurements (\(info.time.totalNumberOfMeasurements) total)") IconAndTextView( icon: storageIcon, text: storageText) IconAndTextView( icon: .iphoneAndArrowForward, text: "\(info.transferBlockSize) Byte Block Size") IconAndTextView( icon: .externaldriveBadgeCheckmark, text: String(format: "0x%02X 0x%02X", UInt8(info.dataChecksum >> 8), UInt8( info.dataChecksum & 0xFF))) } sensorView(info.sensor0, id: 0) sensorView(info.sensor1, id: 1) Spacer() HStack { Spacer() TimelineView(.periodic(from: Date(), by: 1)) { context in Text(updateText) .font(.footnote) .textCase(.uppercase) } Spacer() } } .padding() .navigationTitle("Device Info") .navigationBarTitleDisplayMode(.large) } } } struct DeviceInfoView_Previews: PreviewProvider { static var previews: some View { DeviceInfoView(info: .mock, isPresented: .constant(true)) } }