Switch to HKWorkoutActivity
This commit is contained in:
parent
b8162e6cb9
commit
94fc10f204
@ -19,7 +19,6 @@
|
||||
8850027F2B5C36A700E7D4DB /* Workout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8850027E2B5C36A700E7D4DB /* Workout.swift */; };
|
||||
885002852B5C7AD600E7D4DB /* WorkoutEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 885002842B5C7AD600E7D4DB /* WorkoutEvent.swift */; };
|
||||
885002872B5C7FA900E7D4DB /* HKWorkoutActivityType+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 885002862B5C7FA900E7D4DB /* HKWorkoutActivityType+Extensions.swift */; };
|
||||
8850028B2B5C896C00E7D4DB /* WorkoutActivity.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8850028A2B5C896C00E7D4DB /* WorkoutActivity.swift */; };
|
||||
8850028D2B5D0B5000E7D4DB /* WorkoutDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8850028C2B5D0B5000E7D4DB /* WorkoutDetailView.swift */; };
|
||||
8850028F2B5D0EAF00E7D4DB /* Date+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8850028E2B5D0EAF00E7D4DB /* Date+Extensions.swift */; };
|
||||
885002912B5D0F9200E7D4DB /* HKWorkoutEventType+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 885002902B5D0F9200E7D4DB /* HKWorkoutEventType+Extensions.swift */; };
|
||||
@ -78,7 +77,6 @@
|
||||
8850027E2B5C36A700E7D4DB /* Workout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Workout.swift; sourceTree = "<group>"; };
|
||||
885002842B5C7AD600E7D4DB /* WorkoutEvent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WorkoutEvent.swift; sourceTree = "<group>"; };
|
||||
885002862B5C7FA900E7D4DB /* HKWorkoutActivityType+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "HKWorkoutActivityType+Extensions.swift"; sourceTree = "<group>"; };
|
||||
8850028A2B5C896C00E7D4DB /* WorkoutActivity.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WorkoutActivity.swift; sourceTree = "<group>"; };
|
||||
8850028C2B5D0B5000E7D4DB /* WorkoutDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WorkoutDetailView.swift; sourceTree = "<group>"; };
|
||||
8850028E2B5D0EAF00E7D4DB /* Date+Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Date+Extensions.swift"; sourceTree = "<group>"; };
|
||||
885002902B5D0F9200E7D4DB /* HKWorkoutEventType+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "HKWorkoutEventType+Extensions.swift"; sourceTree = "<group>"; };
|
||||
@ -215,7 +213,6 @@
|
||||
E27BC6892B5FC255003A8873 /* Sample+Unit.swift */,
|
||||
8850027E2B5C36A700E7D4DB /* Workout.swift */,
|
||||
8850027A2B5C35BF00E7D4DB /* Workout+SQLite.swift */,
|
||||
8850028A2B5C896C00E7D4DB /* WorkoutActivity.swift */,
|
||||
E27BC68F2B5FCEA4003A8873 /* WorkoutActivity+SQLite.swift */,
|
||||
885002842B5C7AD600E7D4DB /* WorkoutEvent.swift */,
|
||||
E27BC68D2B5FCBD5003A8873 /* WorkoutEvent+SQLite.swift */,
|
||||
@ -367,7 +364,6 @@
|
||||
E2FDFF1C2B6BD0D20080A7B3 /* HKDatabaseFile+Interface.swift in Sources */,
|
||||
885002A32B5D217600E7D4DB /* MetadataValue.swift in Sources */,
|
||||
E201EC7B2B6275CA005B83D3 /* Metadata.swift in Sources */,
|
||||
8850028B2B5C896C00E7D4DB /* WorkoutActivity.swift in Sources */,
|
||||
E27BC67A2B5D99AC003A8873 /* LocationSample.swift in Sources */,
|
||||
885002952B5D147100E7D4DB /* DetailRow.swift in Sources */,
|
||||
E201EC732B626A30005B83D3 /* WorkoutActivity+Mock.swift in Sources */,
|
||||
|
@ -1,11 +1,12 @@
|
||||
import SwiftUI
|
||||
import HealthKit
|
||||
|
||||
struct ActivityDetailView: View {
|
||||
|
||||
@EnvironmentObject
|
||||
var database: HealthDatabase
|
||||
|
||||
let activity: WorkoutActivity
|
||||
let activity: HKWorkoutActivity
|
||||
|
||||
@State var locations: [LocationSample] = []
|
||||
|
||||
@ -14,11 +15,10 @@ struct ActivityDetailView: View {
|
||||
var body: some View {
|
||||
List {
|
||||
DetailRow("UUID", value: activity.uuid)
|
||||
DetailRow("Primary Activity", value: activity.isPrimaryActivity)
|
||||
DetailRow("Activity", value: activity.activityType)
|
||||
DetailRow("Location", value: activity.locationType)
|
||||
DetailRow("Swimming Location", value: activity.swimmingLocationType)
|
||||
DetailRow("Lap Length", value: activity.lapLength)
|
||||
DetailRow("Activity", value: activity.workoutConfiguration.activityType)
|
||||
DetailRow("Location", value: activity.workoutConfiguration.locationType)
|
||||
DetailRow("Swimming Location", value: activity.workoutConfiguration.swimmingLocationType)
|
||||
DetailRow("Lap Length", value: activity.workoutConfiguration.lapLength)
|
||||
DetailRow("Start", date: activity.startDate)
|
||||
DetailRow("End", date: activity.endDate)
|
||||
DetailRow("Duration", duration: activity.duration)
|
||||
|
@ -1,16 +1,17 @@
|
||||
import SwiftUI
|
||||
import OrderedCollections
|
||||
import HealthKit
|
||||
|
||||
struct ActivitySamplesView: View {
|
||||
|
||||
@EnvironmentObject
|
||||
var database: HealthDatabase
|
||||
|
||||
let activity: WorkoutActivity
|
||||
let activity: HKWorkoutActivity
|
||||
|
||||
@State var samples: [(type: Sample.DataType, samples: [Sample])] = []
|
||||
|
||||
init(activity: WorkoutActivity) {
|
||||
init(activity: HKWorkoutActivity) {
|
||||
self.activity = activity
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
import Foundation
|
||||
import SQLite
|
||||
import CoreLocation
|
||||
import HealthKit
|
||||
|
||||
typealias Database = Connection
|
||||
|
||||
@ -39,19 +40,25 @@ final class HealthDatabase: ObservableObject {
|
||||
}
|
||||
}
|
||||
|
||||
func locationSamples(for activity: WorkoutActivity) throws -> [LocationSample] {
|
||||
try activity.locationSamples(in: database)
|
||||
func locationSamples(for activity: HKWorkoutActivity) throws -> [LocationSample] {
|
||||
try LocationSample.locationSamples(from: activity.startDate, to: activity.currentEndDate, in: database)
|
||||
}
|
||||
|
||||
func samples(for activity: WorkoutActivity) throws -> [Sample.DataType : [Sample]] {
|
||||
try activity.samples(in: database)
|
||||
func locationSampleCount(for activity: HKWorkoutActivity) throws -> Int {
|
||||
try LocationSample.locationSampleCount(from: activity.startDate, to: activity.currentEndDate, in: database)
|
||||
}
|
||||
|
||||
func sampleCount(for activity: WorkoutActivity) throws -> Int {
|
||||
try activity.sampleCount(in: database)
|
||||
func samples(for activity: HKWorkoutActivity) throws -> [Sample.DataType : [Sample]] {
|
||||
try Sample.samples(from: activity.startDate, to: activity.currentEndDate, in: database).reduce(into: [:]) {
|
||||
$0[$1.dataType] = ($0[$1.dataType] ?? []) + [$1]
|
||||
}
|
||||
}
|
||||
|
||||
var activities: [WorkoutActivity] {
|
||||
func sampleCount(for activity: HKWorkoutActivity) throws -> Int {
|
||||
try Sample.sampleCount(from: activity.startDate, to: activity.currentEndDate, in: database)
|
||||
}
|
||||
|
||||
var activities: [HKWorkoutActivity] {
|
||||
workouts.map { $0.activities }.joined().sorted()
|
||||
}
|
||||
|
||||
@ -59,11 +66,11 @@ final class HealthDatabase: ObservableObject {
|
||||
let activities = self.activities
|
||||
var current = activities.first!
|
||||
for next in activities.dropFirst() {
|
||||
let overlap = next.startDate.timeIntervalSince(current.endDate)
|
||||
let overlap = next.startDate.timeIntervalSince(current.currentEndDate)
|
||||
if overlap < 0 {
|
||||
print("Overlap \(-overlap.roundedInt) s:")
|
||||
print(" Activity \(current.activityType.description): \(current.startDate.timeAndDateText) -> \(current.endDate.timeAndDateText)")
|
||||
print(" Activity \(next.activityType.description): \(next.startDate.timeAndDateText) -> \(next.endDate.timeAndDateText)")
|
||||
print(" Activity \(current.workoutConfiguration.activityType.description): \(current.startDate.timeAndDateText) -> \(current.currentEndDate.timeAndDateText)")
|
||||
print(" Activity \(next.workoutConfiguration.activityType.description): \(next.startDate.timeAndDateText) -> \(next.currentEndDate.timeAndDateText)")
|
||||
}
|
||||
current = next
|
||||
}
|
||||
@ -73,3 +80,10 @@ final class HealthDatabase: ObservableObject {
|
||||
self.init(fileUrl: .init(filePath: "/"), database: database)
|
||||
}
|
||||
}
|
||||
|
||||
private extension HKWorkoutActivity {
|
||||
|
||||
var currentEndDate: Date {
|
||||
endDate ?? Date()
|
||||
}
|
||||
}
|
||||
|
@ -31,7 +31,7 @@ extension Workout {
|
||||
let id = row[columnDataId]
|
||||
|
||||
let events = try HKWorkoutEventTable.events(for: id, in: database)
|
||||
let activities = try WorkoutActivity.activities(for: id, in: database)
|
||||
let activities = try WorkoutActivityTable.activities(for: id, in: database)
|
||||
let metadata = try Metadata.metadata(for: id, in: database, keyMap: metadataKeys)
|
||||
return .init(
|
||||
id: id,
|
||||
@ -74,9 +74,14 @@ extension Workout {
|
||||
for event in element.events {
|
||||
try event.insert(in: database, dataId: dataId)
|
||||
}
|
||||
for activity in element.activities {
|
||||
try activity.insert(in: database, dataId: dataId)
|
||||
|
||||
if let activity = element.activities.first {
|
||||
try WorkoutActivityTable.insert(activity, isPrimaryActivity: true, dataId: dataId, in: database)
|
||||
}
|
||||
for activity in element.activities.dropFirst() {
|
||||
try WorkoutActivityTable.insert(activity, isPrimaryActivity: false, dataId: dataId, in: database)
|
||||
}
|
||||
|
||||
for (key, value) in element.metadata {
|
||||
try Metadata.insert(value, for: key, of: dataId, in: database)
|
||||
}
|
||||
|
@ -25,8 +25,8 @@ struct Workout {
|
||||
|
||||
let events: [HKWorkoutEvent]
|
||||
|
||||
let activities: [WorkoutActivity]
|
||||
|
||||
let activities: [HKWorkoutActivity]
|
||||
|
||||
let metadata: OrderedDictionary<Metadata.Key, Metadata.Value>
|
||||
|
||||
var firstActivityDate: Date? {
|
||||
@ -49,10 +49,10 @@ struct Workout {
|
||||
}
|
||||
|
||||
var typeString: String {
|
||||
activities.first?.activityType.description ?? "Unknown activity"
|
||||
activities.first?.workoutConfiguration.activityType.description ?? "Unknown activity"
|
||||
}
|
||||
|
||||
init(id: Int, totalDistance: Double? = nil, goalType: Int? = nil, goal: Double? = nil, condenserVersion: Int? = nil, condenserDate: Date? = nil, events: [HKWorkoutEvent] = [], activities: [WorkoutActivity] = [], metadata: [Metadata.Key : Metadata.Value] = [:]) {
|
||||
init(id: Int, totalDistance: Double? = nil, goalType: Int? = nil, goal: Double? = nil, condenserVersion: Int? = nil, condenserDate: Date? = nil, events: [HKWorkoutEvent] = [], activities: [HKWorkoutActivity] = [], metadata: [Metadata.Key : Metadata.Value] = [:]) {
|
||||
self.id = id
|
||||
self.totalDistance = totalDistance
|
||||
self.goal = .init(goalType: goalType, goal: goal)
|
||||
|
@ -2,7 +2,14 @@ import Foundation
|
||||
import SQLite
|
||||
import HealthKit
|
||||
|
||||
extension WorkoutActivity {
|
||||
extension HKWorkoutActivity: Comparable {
|
||||
|
||||
public static func < (lhs: HKWorkoutActivity, rhs: HKWorkoutActivity) -> Bool {
|
||||
lhs.startDate < rhs.startDate
|
||||
}
|
||||
}
|
||||
|
||||
enum WorkoutActivityTable {
|
||||
|
||||
private static let table = Table("workout_activities")
|
||||
|
||||
@ -30,30 +37,38 @@ extension WorkoutActivity {
|
||||
|
||||
private static let columnMetadata = Expression<Data?>("metadata")
|
||||
|
||||
private static func readAll(in database: Connection) throws -> [Self] {
|
||||
try database.prepare(table).map(from)
|
||||
private static func readAll(in database: Connection) throws -> [HKWorkoutActivity] {
|
||||
try database.prepare(table).map(activity)
|
||||
}
|
||||
|
||||
private static func from(row: Row) throws -> WorkoutActivity {
|
||||
.init(
|
||||
id: row[columnId],
|
||||
uuid: row[columnUUID],
|
||||
isPrimaryActivity: row[columnIsPrimaryActivity],
|
||||
activityType: .init(rawValue: UInt(row[columnActivityType]))!,
|
||||
locationType: .init(rawValue: row[columnLocationType])!,
|
||||
swimmingLocationType: .init(rawValue: row[columnSwimmingLocationType])!,
|
||||
lapLength: try row[columnLapLength].map(lapLength),
|
||||
startDate: Date(timeIntervalSinceReferenceDate: row[columnStartDate]),
|
||||
endDate: Date(timeIntervalSinceReferenceDate: row[columnEndDate]),
|
||||
duration: row[columnDuration],
|
||||
metadata: row[columnMetadata])
|
||||
private static func activity(from row: Row) throws -> HKWorkoutActivity {
|
||||
let configuration = HKWorkoutConfiguration()
|
||||
configuration.lapLength = try row[columnLapLength].map(lapLength)
|
||||
configuration.activityType = .init(rawValue: UInt(row[columnActivityType]))!
|
||||
configuration.locationType = .init(rawValue: row[columnLocationType])!
|
||||
configuration.swimmingLocationType = .init(rawValue: row[columnSwimmingLocationType])!
|
||||
|
||||
let start = Date(timeIntervalSinceReferenceDate: row[columnStartDate])
|
||||
let end = Date(timeIntervalSinceReferenceDate: row[columnEndDate])
|
||||
let uuid = row[columnUUID].uuidString
|
||||
let metadata: [String : Any] = [HKMetadataKeyExternalUUID : uuid]
|
||||
// duration: row[columnDuration]
|
||||
// isPrimaryActivity: row[columnIsPrimaryActivity]
|
||||
|
||||
// metadata: row[columnMetadata]
|
||||
#warning("Decode metadata")
|
||||
return .init(
|
||||
workoutConfiguration: configuration,
|
||||
start: start,
|
||||
end: end,
|
||||
metadata: metadata)
|
||||
}
|
||||
|
||||
static func activities(for workoutId: Int, in database: Connection) throws -> [Self] {
|
||||
try database.prepare(table.filter(columnOwnerId == workoutId)).map(from)
|
||||
static func activities(for workoutId: Int, in database: Connection) throws -> [HKWorkoutActivity] {
|
||||
try database.prepare(table.filter(columnOwnerId == workoutId)).map(activity)
|
||||
}
|
||||
|
||||
static func createTable(in database: Connection) throws {
|
||||
static func create(in database: Connection) throws {
|
||||
//try database.execute("CREATE TABLE workout_activities (ROWID INTEGER PRIMARY KEY AUTOINCREMENT, uuid BLOB UNIQUE NOT NULL, owner_id INTEGER NOT NULL REFERENCES workouts(data_id) ON DELETE CASCADE, is_primary_activity INTEGER NOT NULL, activity_type INTEGER NOT NULL, location_type INTEGER NOT NULL, swimming_location_type INTEGER NOT NULL, lap_length BLOB, start_date REAL NOT NULL, end_date REAL NOT NULL, duration REAL NOT NULL, metadata BLOB)")
|
||||
try database.run(table.create { t in
|
||||
t.column(columnId, primaryKey: .autoincrement)
|
||||
@ -71,30 +86,36 @@ extension WorkoutActivity {
|
||||
})
|
||||
}
|
||||
|
||||
func insert(in database: Connection, dataId: Int) throws {
|
||||
try WorkoutActivity.insert(self, dataId: dataId, in: database)
|
||||
}
|
||||
|
||||
private static func insert(_ element: WorkoutActivity, dataId: Int, in database: Connection) throws {
|
||||
static func insert(_ element: HKWorkoutActivity, isPrimaryActivity: Bool, dataId: Int, in database: Connection) throws {
|
||||
try database.run(table.insert(
|
||||
columnUUID <- element.uuid,
|
||||
columnUUID <- (element.externalUUID ?? element.uuid).uuidString.data(using: .utf8)!,
|
||||
columnOwnerId <- dataId,
|
||||
columnIsPrimaryActivity <- element.isPrimaryActivity,
|
||||
columnActivityType <- Int(element.activityType.rawValue),
|
||||
columnLocationType <- element.locationType.rawValue,
|
||||
columnSwimmingLocationType <- element.swimmingLocationType.rawValue,
|
||||
columnLapLength <- try element.lapLengthData(),
|
||||
columnIsPrimaryActivity <- isPrimaryActivity,
|
||||
columnActivityType <- Int(element.workoutConfiguration.activityType.rawValue),
|
||||
columnLocationType <- element.workoutConfiguration.locationType.rawValue,
|
||||
columnSwimmingLocationType <- element.workoutConfiguration.swimmingLocationType.rawValue,
|
||||
columnLapLength <- try lapLengthData(lapLength: element.workoutConfiguration.lapLength),
|
||||
columnStartDate <- element.startDate.timeIntervalSinceReferenceDate,
|
||||
columnEndDate <- element.endDate.timeIntervalSinceReferenceDate,
|
||||
columnDuration <- element.duration,
|
||||
columnMetadata <- element.metadata)
|
||||
columnEndDate <- element.endDate?.timeIntervalSinceReferenceDate ?? element.startDate.addingTimeInterval(element.duration).timeIntervalSinceReferenceDate,
|
||||
columnDuration <- element.duration)
|
||||
//columnMetadata <- element.metadata)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private extension WorkoutActivity {
|
||||
private extension HKWorkoutActivity {
|
||||
|
||||
func lapLengthData() throws -> Data? {
|
||||
var externalUUID: UUID? {
|
||||
guard let string = metadata?[HKMetadataKeyExternalUUID] as? String else {
|
||||
return nil
|
||||
}
|
||||
return UUID(uuidString: string)
|
||||
}
|
||||
}
|
||||
|
||||
private extension WorkoutActivityTable {
|
||||
|
||||
static func lapLengthData(lapLength: HKQuantity?) throws -> Data? {
|
||||
try lapLength.map { try NSKeyedArchiver.archivedData(withRootObject: $0, requiringSecureCoding: false) }
|
||||
}
|
||||
|
||||
@ -102,3 +123,19 @@ private extension WorkoutActivity {
|
||||
try NSKeyedUnarchiver.unarchivedObject(ofClass: HKQuantity.self, from: data)
|
||||
}
|
||||
}
|
||||
|
||||
private extension Data {
|
||||
|
||||
var uuidString: String {
|
||||
let h = Array(self)
|
||||
let parts = [h[0..<4], h[4..<6], h[6..<8], h[8..<10], h[10..<16]]
|
||||
return parts.map { Data($0).hex }.joined(separator: "-")
|
||||
}
|
||||
}
|
||||
|
||||
private extension UUID {
|
||||
|
||||
init?(data: Data) {
|
||||
self.init(uuidString: data.uuidString)
|
||||
}
|
||||
}
|
||||
|
@ -1,81 +0,0 @@
|
||||
import Foundation
|
||||
import HealthKit
|
||||
|
||||
struct WorkoutActivity {
|
||||
|
||||
private let id: Int
|
||||
|
||||
let uuid: Data
|
||||
|
||||
let isPrimaryActivity: Bool
|
||||
|
||||
let activityType: HKWorkoutActivityType
|
||||
|
||||
let locationType: HKWorkoutSessionLocationType
|
||||
|
||||
let swimmingLocationType: HKWorkoutSwimmingLocationType
|
||||
|
||||
let lapLength: HKQuantity?
|
||||
|
||||
#warning("Fix timezone for dates")
|
||||
let startDate: Date
|
||||
|
||||
let endDate: Date
|
||||
|
||||
let duration: TimeInterval
|
||||
|
||||
let metadata: Data?
|
||||
|
||||
init(id: Int, uuid: Data, isPrimaryActivity: Bool, activityType: HKWorkoutActivityType, locationType: HKWorkoutSessionLocationType, swimmingLocationType: HKWorkoutSwimmingLocationType, lapLength: HKQuantity?, startDate: Date, endDate: Date, duration: TimeInterval, metadata: Data?) {
|
||||
self.id = id
|
||||
self.uuid = uuid
|
||||
self.isPrimaryActivity = isPrimaryActivity
|
||||
self.activityType = activityType
|
||||
self.locationType = locationType
|
||||
self.swimmingLocationType = swimmingLocationType
|
||||
self.lapLength = lapLength
|
||||
self.startDate = startDate
|
||||
self.endDate = endDate
|
||||
self.duration = duration
|
||||
self.metadata = metadata
|
||||
}
|
||||
|
||||
func locationSamples(in database: Database) throws -> [LocationSample] {
|
||||
try LocationSample.locationSamples(from: startDate, to: endDate, in: database)
|
||||
}
|
||||
|
||||
func locationSampleCount(in database: Database) throws -> Int {
|
||||
try LocationSample.locationSampleCount(from: startDate, to: endDate, in: database)
|
||||
}
|
||||
|
||||
func sampleCount(in database: Database) throws -> Int {
|
||||
try Sample.sampleCount(from: startDate, to: endDate, in: database)
|
||||
}
|
||||
|
||||
func samples(in database: Database) throws -> [Sample.DataType : [Sample]] {
|
||||
try Sample.samples(from: startDate, to: endDate, in: database).reduce(into: [:]) {
|
||||
$0[$1.dataType] = ($0[$1.dataType] ?? []) + [$1]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension WorkoutActivity: Equatable {
|
||||
|
||||
static func == (lhs: WorkoutActivity, rhs: WorkoutActivity) -> Bool {
|
||||
lhs.id == rhs.id
|
||||
}
|
||||
}
|
||||
|
||||
extension WorkoutActivity: Comparable {
|
||||
|
||||
static func < (lhs: WorkoutActivity, rhs: WorkoutActivity) -> Bool {
|
||||
lhs.startDate < rhs.startDate
|
||||
}
|
||||
}
|
||||
|
||||
extension WorkoutActivity: Hashable {
|
||||
|
||||
func hash(into hasher: inout Hasher) {
|
||||
hasher.combine(id)
|
||||
}
|
||||
}
|
@ -60,7 +60,7 @@ private extension WorkoutEventMetadata.Element {
|
||||
}
|
||||
|
||||
static func from(key: String, value: Any) -> Self? {
|
||||
if let value = value as? UInt {
|
||||
if let value = value as? UInt64 {
|
||||
return .with {
|
||||
$0.key = key
|
||||
$0.unsignedValue = UInt64(value)
|
||||
|
@ -20,7 +20,7 @@ extension HealthDatabase {
|
||||
|
||||
try Workout.createTable(in: database)
|
||||
try HKWorkoutEventTable.create(in: database)
|
||||
try WorkoutActivity.createTable(in: database)
|
||||
try WorkoutActivityTable.create(in: database)
|
||||
try Metadata.createTables(in: database)
|
||||
|
||||
try Workout.mock1.insert(in: database)
|
||||
|
@ -1,18 +1,20 @@
|
||||
import Foundation
|
||||
import HealthKit
|
||||
|
||||
extension WorkoutActivity {
|
||||
extension HKWorkoutActivity {
|
||||
|
||||
static var mock1: WorkoutActivity = .init(
|
||||
id: 744,
|
||||
uuid: Data(hex: "0e0019a803d541e7b240feb7a360911a")!,
|
||||
isPrimaryActivity: true,
|
||||
activityType: .init(rawValue: 24)!,
|
||||
locationType: .init(rawValue: 3)!,
|
||||
swimmingLocationType: .init(rawValue: 0)!,
|
||||
lapLength: nil,
|
||||
startDate: .init(timeIntervalSinceReferenceDate: 702107518.84307),
|
||||
endDate: .init(timeIntervalSinceReferenceDate: 702143189.432644),
|
||||
duration: 27405.1830769777,
|
||||
metadata: nil)
|
||||
static var mock1: HKWorkoutActivity = {
|
||||
let configuration = HKWorkoutConfiguration()
|
||||
configuration.activityType = .init(rawValue: 24)!
|
||||
configuration.locationType = .init(rawValue: 3)!
|
||||
configuration.swimmingLocationType = .init(rawValue: 0)!
|
||||
configuration.lapLength = nil
|
||||
|
||||
let metadata: [String: Any] = [ HKMetadataKeyExternalUUID : "0E0019A803D541E7B240FEB7A360911A"]
|
||||
return .init(
|
||||
workoutConfiguration: configuration,
|
||||
start: .init(timeIntervalSinceReferenceDate: 702107518.84307),
|
||||
end: .init(timeIntervalSinceReferenceDate: 702143189.432644),
|
||||
metadata: metadata)
|
||||
}()
|
||||
}
|
||||
|
@ -22,7 +22,7 @@ struct WorkoutDetailView: View {
|
||||
Section("Activities") {
|
||||
ForEach(workout.activities, id: \.startDate) { activity in
|
||||
NavigationLink(value: activity) {
|
||||
DetailRow(activity.activityType.description,
|
||||
DetailRow(activity.workoutConfiguration.activityType.description,
|
||||
date: activity.startDate)
|
||||
}
|
||||
|
||||
@ -47,7 +47,7 @@ struct WorkoutDetailView: View {
|
||||
}
|
||||
}
|
||||
.navigationTitle(workout.typeString)
|
||||
.navigationDestination(for: WorkoutActivity.self) { activity in
|
||||
.navigationDestination(for: HKWorkoutActivity.self) { activity in
|
||||
ActivityDetailView(activity: activity)
|
||||
.environmentObject(database)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user