96 lines
2.6 KiB
Swift
96 lines
2.6 KiB
Swift
import Foundation
|
|
import HealthKit
|
|
import SwiftProtobuf
|
|
|
|
extension HKWorkoutEvent: Identifiable {
|
|
|
|
public var id: Double {
|
|
dateInterval.start.timeIntervalSinceReferenceDate * Double(type.rawValue) * dateInterval.duration
|
|
}
|
|
}
|
|
|
|
extension HKWorkoutEventTable {
|
|
|
|
static func decode(metadata data: Data) -> [String : Any] {
|
|
let metadata: WorkoutEventMetadata
|
|
do {
|
|
metadata = try WorkoutEventMetadata(serializedData: data)
|
|
} catch {
|
|
print("Failed to decode event metadata: \(error)")
|
|
print(data.hex)
|
|
return [:]
|
|
}
|
|
|
|
return metadata.elements.reduce(into: [:]) { dict, element in
|
|
guard let value = element.value else {
|
|
print("No value for metadata element \(element)")
|
|
print(data.hex)
|
|
return
|
|
}
|
|
dict[element.key] = value
|
|
}
|
|
}
|
|
|
|
static func encode(metadata: [String : Any]) -> Data? {
|
|
let wrapper = WorkoutEventMetadata.with {
|
|
$0.elements = metadata.compactMap { .from(key: $0.key, value: $0.value) }
|
|
}
|
|
guard !wrapper.elements.isEmpty else {
|
|
return nil
|
|
}
|
|
do {
|
|
return try wrapper.serializedData()
|
|
} catch {
|
|
print("Failed to encode event metadata: \(error)")
|
|
return nil
|
|
}
|
|
}
|
|
}
|
|
|
|
private extension WorkoutEventMetadata.Element {
|
|
|
|
var value: Any? {
|
|
if hasUnsignedValue {
|
|
return unsignedValue
|
|
}
|
|
if hasQuantity {
|
|
return HKQuantity(unit: .init(from: quantity.unit), doubleValue: quantity.value)
|
|
}
|
|
return UInt(0)
|
|
}
|
|
|
|
static func from(key: String, value: Any) -> Self? {
|
|
if let value = value as? UInt {
|
|
return .with {
|
|
$0.key = key
|
|
$0.unsignedValue = UInt64(value)
|
|
}
|
|
}
|
|
guard let value = value as? HKQuantity else {
|
|
print("Unknown value type for metadata key \(key): \(value)")
|
|
return nil
|
|
}
|
|
|
|
let number: Double
|
|
let unit: String
|
|
if value.is(compatibleWith: .meter()) {
|
|
number = value.doubleValue(for: .meter())
|
|
unit = "m"
|
|
} else if value.is(compatibleWith: .second()) {
|
|
number = value.doubleValue(for: .second())
|
|
unit = "s"
|
|
} else {
|
|
print("Unhandled quantity type for metadata key \(key): \(value)")
|
|
return nil
|
|
}
|
|
|
|
return .with { el in
|
|
el.key = key
|
|
el.quantity = .with {
|
|
$0.value = number
|
|
$0.unit = unit
|
|
}
|
|
}
|
|
}
|
|
}
|