Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Hard Crash when addQuantity is used. #57

Open
Mravuri96 opened this issue Mar 16, 2023 · 1 comment
Open

Hard Crash when addQuantity is used. #57

Mravuri96 opened this issue Mar 16, 2023 · 1 comment

Comments

@Mravuri96
Copy link

Mravuri96 commented Mar 16, 2023

Describe the bug
Using the await HealthKitReporter.addQuantity function causes the app to hard crash.

To Reproduce

final preferredUnits = await HealthKitReporter.preferredUnits(
          [
            QuantityType.activeEnergyBurned,
            QuantityType.stepCount,
            QuantityType.distanceCycling
          ],
        );

final calorieUnit = preferredUnits.first;
final distanceUnit = preferredUnits.last;

final workoutHarmonized = WorkoutHarmonized(
          WorkoutActivityType.cycling,
          200,
          calorieUnit.unit,
          1,
          distanceUnit.unit,
          null,
          '',
          null,
          '',
          null,
        );

final workout = Workout(
          '',
          '',
          DateTime.now()
              .subtract(const Duration(seconds: 60))
              .millisecondsSinceEpoch,
          DateTime.now().millisecondsSinceEpoch,
          null,
          sourceRevision,
          workoutHarmonized,
          workout.duration,
          [],
        );

await HealthKitReporter.save(workout);

final energySample = Quantity(
          '',
          QuantityType.activeEnergyBurned.identifier,
          DateTime.now()
              .subtract(const Duration(seconds: 30))
              .millisecondsSinceEpoch,
          DateTime.now().millisecondsSinceEpoch,
          null,
          sourceRevision,
          QuantityHarmonized(
            100,
            calorieUnit.unit,
            null,
          ),
        );

await HealthKitReporter.save(energySample); //Works, but does not fill the rings if an Apple Watch is paired.

// Need this to fill the workout rings 
await HealthKitReporter.addQuantity([energySample], workout); // Crashes the app

Error

*** Terminating app due to uncaught exception '_HKObjectValidationFailureException', reason: 'Type HKSample can not have endDate of NSDate.distantFuture'
*** First throw call stack:
(0x1d124ce38 0x1ca3e78d8 0x1d133cc28 0x1e6b5f6ec 0x1e6b5f4b8 0x1e6b5f3a8 0x1e6b61d7c 0x1016f8f04 0x1016f8b94 0x1016db2c8 0x1cb0348dc 0x1016db0a4 0x101926e98 0x10191d114 0x101927c98 0x105244b14 0x104d2fa8c 0x1d8863460 0x1d8864f88 0x1d88737f4 0x1d8873444 0x1d12dd6c8 0x1d12bf02c 0x1d12c3eb0 0x20b4b9368 0x1d37b9668 0x1d37b92cc 0x100d5e25c 0x1efbbc960)
libc++abi: terminating with uncaught exception of type NSException
* thread #1, queue = 'com.apple.main-thread', stop reason = signal SIGABRT
    frame #0: 0x000000020ed67674 libsystem_kernel.dylib`__pthread_kill + 8
libsystem_kernel.dylib`:
->  0x20ed67674 <+8>:  b.lo   0x20ed67694               ; <+40>
    0x20ed67678 <+12>: pacibsp
    0x20ed6767c <+16>: stp    x29, x30, [sp, #-0x10]!
    0x20ed67680 <+20>: mov    x29, sp
Target 0: (Runner) stopped.
Lost connection to device.

Swift Equivalent

private let healthStore = HKHealthStore()

let workout = HKWorkout(
        activityType: .cycling,
        start: Date.now,
        end: Date().withAddedMinutes(minutes: 1),
        workoutEvents: [],
        totalEnergyBurned: HKQuantity(unit: .largeCalorie(), doubleValue: 200),
        totalDistance: HKQuantity(unit: .mile(), doubleValue: 1),
        metadata: nil
      )

 healthStore.save(workout) ... //Works

let energySample  = HKQuantitySample(
      type: HKObjectType.quantityType(forIdentifier: .activeEnergyBurned)!,
      quantity: HKQuantity(unit: .largeCalorie(), doubleValue: 100),
      start: Date.now,
      end: Date.now.withAddedMinutes(minutes: 0.5))
)

healthStore.add([energySample], to: workout) // Works even with future end date.
@Mravuri96
Copy link
Author

Mravuri96 commented Mar 21, 2023

@kvs-coder I was able to fix this in this repo. But since Workout.make always overwrites the uuid, the quantities can never be added to an already existing workout. I am also assuming addCategory is also broken.

private func addQuantity(
reporter: HealthKitReporter,
arguments: [String: Any],
result: @escaping FlutterResult
) {
guard
let quantity = arguments["quantities"] as? [[String: Any]],
let workout = arguments["workout"] as? [String: Any]
else {
throwParsingArgumentsError(result: result, arguments: arguments)
return
}
let device = arguments["device"] as? [String: Any]
do {
reporter.writer.addQuantitiy(try quantity.map {
try Quantity.make(from: $0)
},
from: device != nil
? try Device.make(from: device!)
: nil,
to: try Workout.make(from: workout)) { (success, error) in
guard error == nil else {
result(
FlutterError(
code: "AddQuantity",
message: "Error in addQuantity",
details: error.debugDescription
)
)
return
}
result(success)
}
} catch {
throwPlatformError(result: result, error: error)
}
}

Fix

  private func addQuantity(
    reporter: HealthKitReporter,
    arguments: [String: Any],
    result: @escaping FlutterResult
  ) {
    guard
      let quantity = arguments["quantities"] as? [[String: Any]],
      let workout = arguments["workout"] as? [String: Any]
    else {
      throwParsingArgumentsError(result: result, arguments: arguments)
      return
    }

    let device = arguments["device"] as? [String: Any]
    do {
      let w = try Workout.make(from: workout)
      reporter.writer.addQuantitiy(
        try quantity.map {
          let q = try Quantity.make(from: $0)
          return q.copyWith(
            startTimestamp: q.startTimestamp.secondsSince1970,
            endTimestamp: q.endTimestamp.secondsSince1970
          )
        },
        from: device != nil
          ? try Device.make(from: device!)
          : nil,
        to: w.copyWith(
          startTimestamp: w.startTimestamp.secondsSince1970,
          endTimestamp: w.endTimestamp.secondsSince1970,
          workoutEvents: w.workoutEvents.map { event in
            event.copyWith(
              startTimestamp: event.startTimestamp.secondsSince1970,
              endTimestamp: event.endTimestamp.secondsSince1970
            )
          }
        )
      ) { (success, error) in
        guard error == nil else {
          result(
            FlutterError(
              code: "AddQuantity",
              message: "Error in addQuantity",
              details: error.debugDescription
            )
          )
          return
        }
        result(success)
      }
    } catch {
      throwPlatformError(result: result, error: error)
    }
  }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant