kvs-coder / health_kit_reporter

A Flutter wrapper for the HealthKitReporter library
https://pub.dev/packages/health_kit_reporter
MIT License
38 stars 38 forks source link

Hard Crash when addQuantity is used. #57

Open Mravuri96 opened 1 year ago

Mravuri96 commented 1 year ago

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 commented 1 year ago

@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.

https://github.com/kvs-coder/health_kit_reporter/blob/595484122318d2ce534e1fb566a9c3b97855c7bc/ios/Classes/Extensions%2BSwiftHealthKitReporterPlugin.swift#L1299-L1335

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)
    }
  }