Show all points for day
This commit is contained in:
parent
2cb94a12be
commit
7cd697fb01
@ -41,6 +41,7 @@
|
|||||||
E2A553F92A399F58005204C3 /* Log.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2A553F82A399F58005204C3 /* Log.swift */; };
|
E2A553F92A399F58005204C3 /* Log.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2A553F82A399F58005204C3 /* Log.swift */; };
|
||||||
E2A553FB2A39C82D005204C3 /* LogView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2A553FA2A39C82D005204C3 /* LogView.swift */; };
|
E2A553FB2A39C82D005204C3 /* LogView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2A553FA2A39C82D005204C3 /* LogView.swift */; };
|
||||||
E2A553FD2A39C86B005204C3 /* LogEntry.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2A553FC2A39C86B005204C3 /* LogEntry.swift */; };
|
E2A553FD2A39C86B005204C3 /* LogEntry.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2A553FC2A39C86B005204C3 /* LogEntry.swift */; };
|
||||||
|
E2A553FF2A3A1024005204C3 /* DayView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2A553FE2A3A1024005204C3 /* DayView.swift */; };
|
||||||
/* End PBXBuildFile section */
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
/* Begin PBXFileReference section */
|
/* Begin PBXFileReference section */
|
||||||
@ -77,6 +78,7 @@
|
|||||||
E2A553F82A399F58005204C3 /* Log.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Log.swift; sourceTree = "<group>"; };
|
E2A553F82A399F58005204C3 /* Log.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Log.swift; sourceTree = "<group>"; };
|
||||||
E2A553FA2A39C82D005204C3 /* LogView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogView.swift; sourceTree = "<group>"; };
|
E2A553FA2A39C82D005204C3 /* LogView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogView.swift; sourceTree = "<group>"; };
|
||||||
E2A553FC2A39C86B005204C3 /* LogEntry.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogEntry.swift; sourceTree = "<group>"; };
|
E2A553FC2A39C86B005204C3 /* LogEntry.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogEntry.swift; sourceTree = "<group>"; };
|
||||||
|
E2A553FE2A3A1024005204C3 /* DayView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DayView.swift; sourceTree = "<group>"; };
|
||||||
/* End PBXFileReference section */
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
/* Begin PBXFrameworksBuildPhase section */
|
/* Begin PBXFrameworksBuildPhase section */
|
||||||
@ -178,6 +180,7 @@
|
|||||||
88404DDC2A2F587400D30244 /* HistoryListRow.swift */,
|
88404DDC2A2F587400D30244 /* HistoryListRow.swift */,
|
||||||
88404DDE2A2F68E100D30244 /* TemperatureDayOverview.swift */,
|
88404DDE2A2F68E100D30244 /* TemperatureDayOverview.swift */,
|
||||||
E2A553FA2A39C82D005204C3 /* LogView.swift */,
|
E2A553FA2A39C82D005204C3 /* LogView.swift */,
|
||||||
|
E2A553FE2A3A1024005204C3 /* DayView.swift */,
|
||||||
);
|
);
|
||||||
path = Views;
|
path = Views;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -303,6 +306,7 @@
|
|||||||
88404DD82A2F381B00D30244 /* HistoryList.swift in Sources */,
|
88404DD82A2F381B00D30244 /* HistoryList.swift in Sources */,
|
||||||
88CDE07E2A28AFF400114294 /* DeviceInfoView.swift in Sources */,
|
88CDE07E2A28AFF400114294 /* DeviceInfoView.swift in Sources */,
|
||||||
88404DDD2A2F587400D30244 /* HistoryListRow.swift in Sources */,
|
88404DDD2A2F587400D30244 /* HistoryListRow.swift in Sources */,
|
||||||
|
E2A553FF2A3A1024005204C3 /* DayView.swift in Sources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
Binary file not shown.
@ -142,16 +142,16 @@ final class BluetoothClient: ObservableObject {
|
|||||||
guard let deviceInfo else {
|
guard let deviceInfo else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
guard !deviceInfo.hasDeviceStartTimeSet || deviceInfo.clockOffset > minimumOffsetToUpdateDeviceClock else {
|
guard !deviceInfo.hasDeviceStartTimeSet || abs(deviceInfo.clockOffset) > minimumOffsetToUpdateDeviceClock else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
guard !openRequests.contains(where: { if case .setDeviceStartTime = $0 { return true }; return false }) else {
|
guard !openRequests.contains(where: { if case .setDeviceStartTime = $0 { return true }; return false }) else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
let time = deviceInfo.deviceStartTime.seconds
|
let time = deviceInfo.calculatedDeviceStartTime.seconds
|
||||||
addRequest(.setDeviceStartTime(deviceStartTimeSeconds: time))
|
addRequest(.setDeviceStartTime(deviceStartTimeSeconds: time))
|
||||||
log.info("Setting device start time to \(time) s (\(Date().seconds) current)")
|
log.info("Setting device start time to \(time) s (correcting offset of \(Int(deviceInfo.clockOffset)) s)")
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: Data transfer
|
// MARK: Data transfer
|
||||||
|
@ -52,9 +52,14 @@ struct DeviceInfo {
|
|||||||
|
|
||||||
var clockOffset: TimeInterval {
|
var clockOffset: TimeInterval {
|
||||||
// Measurements are performed on device start (-1) and also count next measurement (+1)
|
// Measurements are performed on device start (-1) and also count next measurement (+1)
|
||||||
let nextMeasurementTime = deviceStartTime.adding(seconds: (totalNumberOfMeasurements) * measurementInterval)
|
let nextMeasurementTime = deviceStartTime.adding(seconds: totalNumberOfMeasurements * measurementInterval)
|
||||||
return nextMeasurement.timeIntervalSince(nextMeasurementTime)
|
return nextMeasurement.timeIntervalSince(nextMeasurementTime)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var calculatedDeviceStartTime: Date {
|
||||||
|
let runtime = totalNumberOfMeasurements * measurementInterval
|
||||||
|
return nextMeasurement.adding(seconds: -runtime)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension DeviceInfo {
|
extension DeviceInfo {
|
||||||
|
@ -104,7 +104,7 @@ struct ContentView: View {
|
|||||||
self.showHistory = true
|
self.showHistory = true
|
||||||
} label: {
|
} label: {
|
||||||
TemperatureHistoryChart(points: $storage.recentMeasurements)
|
TemperatureHistoryChart(points: $storage.recentMeasurements)
|
||||||
.frame(height: 150)
|
.frame(height: 300)
|
||||||
.background(Color.white.opacity(0.1))
|
.background(Color.white.opacity(0.1))
|
||||||
.cornerRadius(8)
|
.cornerRadius(8)
|
||||||
}
|
}
|
||||||
|
@ -63,6 +63,7 @@ final class TemperatureStorage: ObservableObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ensureExistenceOfFolder()
|
ensureExistenceOfFolder()
|
||||||
|
recalculateDailyCounts()
|
||||||
}
|
}
|
||||||
|
|
||||||
private func ensureExistenceOfFolder() {
|
private func ensureExistenceOfFolder() {
|
||||||
@ -105,15 +106,15 @@ final class TemperatureStorage: ObservableObject {
|
|||||||
}
|
}
|
||||||
let yesterdayValues = loadMeasurements(for: dateIndexOfStart)
|
let yesterdayValues = loadMeasurements(for: dateIndexOfStart)
|
||||||
.filter { $0.date >= startDate }
|
.filter { $0.date >= startDate }
|
||||||
recentMeasurements = yesterdayValues + todayValues
|
recentMeasurements = todayValues + yesterdayValues
|
||||||
log.info("Loaded \(recentMeasurements.count) recent measurements")
|
log.info("Loaded \(recentMeasurements.count) recent measurements")
|
||||||
}
|
}
|
||||||
|
|
||||||
private func updateLastMeasurements(_ measurements: [TemperatureMeasurement]) {
|
private func updateLastMeasurements(_ measurements: [TemperatureMeasurement]) {
|
||||||
let startDate = Date().addingTimeInterval(-lastValueInterval).seconds
|
let startDate = Date().addingTimeInterval(-lastValueInterval).seconds
|
||||||
let new = recentMeasurements + measurements
|
recentMeasurements = (measurements + recentMeasurements)
|
||||||
recentMeasurements = Array(new.drop { $0.id < startDate })
|
.filter { $0.id > startDate }
|
||||||
log.info("\(recentMeasurements.count) recent measurements (of \(measurements.count) new entries)")
|
log.info("\(recentMeasurements.count) recent measurements (with \(measurements.count) new entries)")
|
||||||
}
|
}
|
||||||
|
|
||||||
private func loadMeasurements(for date: Date) -> [TemperatureMeasurement] {
|
private func loadMeasurements(for date: Date) -> [TemperatureMeasurement] {
|
||||||
@ -143,17 +144,19 @@ final class TemperatureStorage: ObservableObject {
|
|||||||
|
|
||||||
func add(_ measurements: [TemperatureMeasurement]) {
|
func add(_ measurements: [TemperatureMeasurement]) {
|
||||||
let lastDate = self.newestMeasurementDate.seconds
|
let lastDate = self.newestMeasurementDate.seconds
|
||||||
let newerValues = measurements.filter { $0.id > lastDate }
|
let newerValues: [TemperatureMeasurement] = measurements.filter { $0.id > lastDate }.reversed()
|
||||||
let newValues = newerValues.splitByDate()
|
let newValues = newerValues.splitByDate()
|
||||||
log.info("Adding \(newValues.count) of \(measurements.count) measurements")
|
log.info("Adding \(newerValues.count) of \(measurements.count) measurements")
|
||||||
|
|
||||||
for (dateIndex, values) in newValues {
|
for (dateIndex, values) in newValues {
|
||||||
let count = saveNew(values, for: dateIndex)
|
let count = saveNew(values, for: dateIndex)
|
||||||
setDailyCount(count, for: dateIndex)
|
setDailyCount(count, for: dateIndex)
|
||||||
//log.info("Day \(dateIndex): \(count) values")
|
|
||||||
}
|
}
|
||||||
saveDailyCounts()
|
saveDailyCounts()
|
||||||
updateLastMeasurements(measurements)
|
updateLastMeasurements(measurements)
|
||||||
|
if let newest = newerValues.max()?.id {
|
||||||
|
newestMeasurementTime = newest
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func removeMeasurements(for dateIndex: Int) {
|
func removeMeasurements(for dateIndex: Int) {
|
||||||
@ -176,7 +179,7 @@ final class TemperatureStorage: ObservableObject {
|
|||||||
*/
|
*/
|
||||||
private func saveNew(_ measurements: [TemperatureMeasurement], for dateIndex: Int) -> Int {
|
private func saveNew(_ measurements: [TemperatureMeasurement], for dateIndex: Int) -> Int {
|
||||||
let fileName = fileName(for: dateIndex)
|
let fileName = fileName(for: dateIndex)
|
||||||
let values = loadMeasurements(from: fileName) + measurements
|
let values = measurements + loadMeasurements(from: fileName)
|
||||||
save(values, for: fileName)
|
save(values, for: fileName)
|
||||||
return values.count
|
return values.count
|
||||||
}
|
}
|
||||||
@ -184,7 +187,7 @@ final class TemperatureStorage: ObservableObject {
|
|||||||
private func save(_ measurements: [TemperatureMeasurement], for fileName: String) {
|
private func save(_ measurements: [TemperatureMeasurement], for fileName: String) {
|
||||||
let fileUrl = fileUrl(for: fileName)
|
let fileUrl = fileUrl(for: fileName)
|
||||||
do {
|
do {
|
||||||
let data = try BinaryEncoder.encode(measurements.sorted())
|
let data = try BinaryEncoder.encode(measurements.sorted().reversed())
|
||||||
try data.write(to: fileUrl)
|
try data.write(to: fileUrl)
|
||||||
} catch {
|
} catch {
|
||||||
log.error("Failed to save \(fileName): \(error)")
|
log.error("Failed to save \(fileName): \(error)")
|
||||||
@ -203,10 +206,7 @@ final class TemperatureStorage: ObservableObject {
|
|||||||
|
|
||||||
private func add(dailyCount count: Int, for dateIndex: Int) {
|
private func add(dailyCount count: Int, for dateIndex: Int) {
|
||||||
let entry = MeasurementDailyCount(dateIndex: dateIndex, count: count)
|
let entry = MeasurementDailyCount(dateIndex: dateIndex, count: count)
|
||||||
guard let index = dailyMeasurementCounts.firstIndex(where: { $0.dateIndex < dateIndex }) else {
|
let index = dailyMeasurementCounts.firstIndex(where: { $0.dateIndex > dateIndex }) ?? 0
|
||||||
dailyMeasurementCounts.append(entry)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
dailyMeasurementCounts.insert(entry, at: index)
|
dailyMeasurementCounts.insert(entry, at: index)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -240,8 +240,10 @@ final class TemperatureStorage: ObservableObject {
|
|||||||
self.dailyMeasurementCounts = measurements.reduce(into: [Int: Int]()) { counts, value in
|
self.dailyMeasurementCounts = measurements.reduce(into: [Int: Int]()) { counts, value in
|
||||||
let index = value.date.dateIndex
|
let index = value.date.dateIndex
|
||||||
counts[index] = (counts[index] ?? 0) + 1
|
counts[index] = (counts[index] ?? 0) + 1
|
||||||
}.map { MeasurementDailyCount(dateIndex: $0.key, count: $0.value) }
|
}
|
||||||
.sorted()
|
.map { MeasurementDailyCount(dateIndex: $0.key, count: $0.value) }
|
||||||
|
.sorted()
|
||||||
|
.reversed()
|
||||||
}
|
}
|
||||||
|
|
||||||
func recalculateDailyCounts() {
|
func recalculateDailyCounts() {
|
||||||
@ -260,13 +262,13 @@ final class TemperatureStorage: ObservableObject {
|
|||||||
self.dailyMeasurementCounts = newValues
|
self.dailyMeasurementCounts = newValues
|
||||||
.map { .init(dateIndex: $0.key, count: $0.value) }
|
.map { .init(dateIndex: $0.key, count: $0.value) }
|
||||||
.sorted()
|
.sorted()
|
||||||
|
.reversed()
|
||||||
log.info("Daily counts recalculated from \(files.count) files")
|
log.info("Daily counts recalculated from \(files.count) files")
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
log.error("Failed to load daily counts: \(error)")
|
log.error("Failed to load daily counts: \(error)")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private extension Array where Element == TemperatureMeasurement {
|
private extension Array where Element == TemperatureMeasurement {
|
||||||
|
@ -56,12 +56,16 @@ final class TemperatureDataTransfer {
|
|||||||
|
|
||||||
func add(data: Data, offset: Int, count: Int) {
|
func add(data: Data, offset: Int, count: Int) {
|
||||||
guard currentByteIndex == offset else {
|
guard currentByteIndex == offset else {
|
||||||
log.warning("Discarding \(data.count) bytes at offset \(offset), expected \(currentByteIndex)")
|
log.warning("Transfer: Discarding \(data.count) bytes at offset \(offset), expected \(currentByteIndex)")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if data.count != count {
|
||||||
|
log.warning("Transfer: Expected \(count) bytes, received only \(data.count)")
|
||||||
|
}
|
||||||
dataBuffer.append(data)
|
dataBuffer.append(data)
|
||||||
currentByteIndex += data.count
|
currentByteIndex += data.count
|
||||||
processBytes()
|
processBytes()
|
||||||
|
log.info("Transfer: \(currentByteIndex) bytes (added \(data.count)), \(measurements.count) points")
|
||||||
}
|
}
|
||||||
|
|
||||||
private func processBytes() {
|
private func processBytes() {
|
||||||
@ -83,6 +87,10 @@ final class TemperatureDataTransfer {
|
|||||||
|
|
||||||
func completeTransfer() {
|
func completeTransfer() {
|
||||||
processBytes()
|
processBytes()
|
||||||
|
if !dataBuffer.isEmpty {
|
||||||
|
log.warning("\(dataBuffer.count) bytes remaining in transfer buffer")
|
||||||
|
}
|
||||||
|
log.info("Transfer: \(currentByteIndex) bytes, \(measurements.count) points")
|
||||||
}
|
}
|
||||||
|
|
||||||
private func addRelative(byte: UInt8) {
|
private func addRelative(byte: UInt8) {
|
||||||
|
@ -27,7 +27,7 @@ struct TemperatureMeasurement: Identifiable {
|
|||||||
return sensor1.optionalValue
|
return sensor1.optionalValue
|
||||||
}
|
}
|
||||||
guard let s1 = sensor1.optionalValue else {
|
guard let s1 = sensor1.optionalValue else {
|
||||||
return nil
|
return s0
|
||||||
}
|
}
|
||||||
return max(s0, s1)
|
return max(s0, s1)
|
||||||
}
|
}
|
||||||
@ -37,10 +37,27 @@ struct TemperatureMeasurement: Identifiable {
|
|||||||
return sensor1.optionalValue
|
return sensor1.optionalValue
|
||||||
}
|
}
|
||||||
guard let s1 = sensor1.optionalValue else {
|
guard let s1 = sensor1.optionalValue else {
|
||||||
return nil
|
return s0
|
||||||
}
|
}
|
||||||
return min(s0, s1)
|
return min(s0, s1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var averageValue: Double? {
|
||||||
|
guard let s0 = sensor0.optionalValue else {
|
||||||
|
return sensor1.optionalValue
|
||||||
|
}
|
||||||
|
guard let s1 = sensor1.optionalValue else {
|
||||||
|
return s0
|
||||||
|
}
|
||||||
|
return (s0 + s1) / 2
|
||||||
|
}
|
||||||
|
|
||||||
|
var displayText: String {
|
||||||
|
guard let averageValue else {
|
||||||
|
return "-"
|
||||||
|
}
|
||||||
|
return String(format: "%.1f °C", averageValue)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension TemperatureMeasurement {
|
extension TemperatureMeasurement {
|
||||||
|
38
TempTrack/Views/DayView.swift
Normal file
38
TempTrack/Views/DayView.swift
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
private let df: DateFormatter = {
|
||||||
|
let df = DateFormatter()
|
||||||
|
df.dateStyle = .short
|
||||||
|
df.timeStyle = .medium
|
||||||
|
return df
|
||||||
|
}()
|
||||||
|
|
||||||
|
struct DayView: View {
|
||||||
|
|
||||||
|
let dateIndex: Int
|
||||||
|
|
||||||
|
@EnvironmentObject
|
||||||
|
var storage: TemperatureStorage
|
||||||
|
|
||||||
|
var entries: [TemperatureMeasurement] {
|
||||||
|
storage.loadMeasurements(for: dateIndex)
|
||||||
|
}
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
TemperatureDayOverview(points: entries)
|
||||||
|
List(entries) { entry in
|
||||||
|
HStack {
|
||||||
|
Text(df.string(from: entry.date))
|
||||||
|
Spacer()
|
||||||
|
Text(entry.displayText)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct DayView_Previews: PreviewProvider {
|
||||||
|
static var previews: some View {
|
||||||
|
DayView(dateIndex: Date.now.dateIndex)
|
||||||
|
.environmentObject(TemperatureStorage.mock)
|
||||||
|
}
|
||||||
|
}
|
@ -9,16 +9,17 @@ struct HistoryList: View {
|
|||||||
NavigationView {
|
NavigationView {
|
||||||
List(storage.dailyMeasurementCounts) { day in
|
List(storage.dailyMeasurementCounts) { day in
|
||||||
NavigationLink(destination: {
|
NavigationLink(destination: {
|
||||||
TemperatureDayOverview(storage: storage, dateIndex: day.dateIndex)
|
DayView(dateIndex: day.dateIndex)
|
||||||
|
.environmentObject(storage)
|
||||||
}) {
|
}) {
|
||||||
HistoryListRow(entry: day)
|
HistoryListRow(entry: day)
|
||||||
.swipeActions(edge: .trailing, allowsFullSwipe: true) {
|
.swipeActions(edge: .trailing, allowsFullSwipe: true) {
|
||||||
Button {
|
Button {
|
||||||
deleteRow(for: day.dateIndex)
|
deleteRow(for: day.dateIndex)
|
||||||
} label: {
|
} label: {
|
||||||
Label("Delete", systemSymbol: .pencil)
|
Label("Delete", systemSymbol: .trash)
|
||||||
}
|
}
|
||||||
.tint(.purple)
|
.tint(.red)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,17 +1,17 @@
|
|||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
|
||||||
|
private let df: DateFormatter = {
|
||||||
|
let df = DateFormatter()
|
||||||
|
df.dateStyle = .short
|
||||||
|
df.timeStyle = .medium
|
||||||
|
return df
|
||||||
|
}()
|
||||||
|
|
||||||
struct LogView: View {
|
struct LogView: View {
|
||||||
|
|
||||||
@EnvironmentObject
|
@EnvironmentObject
|
||||||
var log: Log
|
var log: Log
|
||||||
|
|
||||||
private let df: DateFormatter = {
|
|
||||||
let df = DateFormatter()
|
|
||||||
df.dateStyle = .short
|
|
||||||
df.timeStyle = .medium
|
|
||||||
return df
|
|
||||||
}()
|
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
List(log.logEntries) { entry in
|
List(log.logEntries) { entry in
|
||||||
VStack(alignment: .leading) {
|
VStack(alignment: .leading) {
|
||||||
|
@ -2,14 +2,14 @@ import SwiftUI
|
|||||||
import Charts
|
import Charts
|
||||||
|
|
||||||
struct TemperatureDayOverview: View {
|
struct TemperatureDayOverview: View {
|
||||||
|
|
||||||
let storage: TemperatureStorage
|
let points: [TemperatureMeasurement]
|
||||||
|
|
||||||
@State
|
init(points: [TemperatureMeasurement]) {
|
||||||
var points: [TemperatureMeasurement] = []
|
self.points = points
|
||||||
|
}
|
||||||
|
|
||||||
init(storage: TemperatureStorage, dateIndex: Int) {
|
init(storage: TemperatureStorage, dateIndex: Int) {
|
||||||
self.storage = storage
|
|
||||||
let points = storage.loadMeasurements(for: dateIndex)
|
let points = storage.loadMeasurements(for: dateIndex)
|
||||||
self.points = points
|
self.points = points
|
||||||
update()
|
update()
|
||||||
|
@ -34,7 +34,7 @@ struct TemperatureHistoryChart: View {
|
|||||||
.chartXAxis(.hidden)
|
.chartXAxis(.hidden)
|
||||||
.chartLegend(.hidden)
|
.chartLegend(.hidden)
|
||||||
.chartYAxis {
|
.chartYAxis {
|
||||||
AxisMarks(position: .trailing, values: .automatic) { value in
|
AxisMarks(position: .trailing, values: .stride(by: 10)) { value in
|
||||||
AxisValueLabel(multiLabelAlignment: .trailing) {
|
AxisValueLabel(multiLabelAlignment: .trailing) {
|
||||||
if let intValue = value.as(Int.self) {
|
if let intValue = value.as(Int.self) {
|
||||||
Text("\(intValue)°")
|
Text("\(intValue)°")
|
||||||
|
Loading…
Reference in New Issue
Block a user