compiler-generated crash in Swift in my react-native app

My healthkit swift code keeps crashing in the background in production, and I can’t figure it out for the life of me. I am fairly new to Swift, so maybe I am making a fundamental error in my implementation.

Features of the crash:

  • It seems to happen only in production. (our internal test program contains 10 devices only (so maybe it is co-incidence that it doesn’t get picked up there).
  • Occurs in iOS versions – 10,11,12,13
  • Occurs for a very small set of users (1.5% of active audience), but very frequently for these same users.

Please find the crash log from my Crashlytics account below.

Crashed: com.facebook.react.HKManagerQueue
0  stepapp                      0x100f4f324 specialized HKManager.getTotal(_:typeStr:unit:options:completion:) + 4372984612 (<compiler-generated>:4372984612)
1  stepapp                      0x100f52e04 HKManager._getTotals(_:completion:) + 397 (HKManager.swift:397)
2  stepapp                      0x100f532ac @objc HKManager.getTotals(_:resolver:rejecter:) + 4373000876 (<compiler-generated>:4373000876)
3  CoreFoundation                 0x1af698c20 __invoking___ + 144
4  CoreFoundation                 0x1af568d30 -[NSInvocation invoke] + 300
5  CoreFoundation                 0x1af569908 -[NSInvocation invokeWithTarget:] + 76
6  stepapp                      0x101184e6c -[RCTModuleMethod invokeWithBridge:module:arguments:] + 241556
7  stepapp                      0x101187248 facebook::react::invokeInner(RCTBridge*, RCTModuleData*, unsigned int, folly::dynamic const&) + 250736
8  stepapp                      0x101186fac invocation function for block in facebook::react::RCTNativeModule::invoke(unsigned int, folly::dynamic&&, int) + 250068
9  libdispatch.dylib              0x1af35e610 _dispatch_call_block_and_release + 24
10 libdispatch.dylib              0x1af35f184 _dispatch_client_callout + 16
11 libdispatch.dylib              0x1af30b404 _dispatch_lane_serial_drain$VARIANT$mp + 608
12 libdispatch.dylib              0x1af30bdf8 _dispatch_lane_invoke$VARIANT$mp + 420
13 libdispatch.dylib              0x1af315314 _dispatch_workloop_worker_thread + 588
14 libsystem_pthread.dylib        0x1af3aeb88 _pthread_wqthread + 276
15 libsystem_pthread.dylib        0x1af3b1760 start_wqthread + 8

I have attached my implementation below, which contains the line mentioned in the crash logs

func _getTotals(_ options: Dictionary<String, Any>, completion: @escaping (Dictionary<String, Double>?) -> Void) {
    var stepsDone = false;
    var distanceDone = false;
    var caloriesDone = false;

    let steps = HKQuantityType.quantityType(forIdentifier: .stepCount);
    let distance = HKQuantityType.quantityType(forIdentifier: .distanceWalkingRunning);
    let calories = HKQuantityType.quantityType(forIdentifier: .activeEnergyBurned);

    var results = Dictionary<String, Double>();

    // 👇 THIS IS LINE 397 which is indicated in the crash report above
    self.getTotal(steps!, typeStr: HKManager.STEP_TYPE_STR, unit: HKUnit.count(), options: options) { (totalSteps, error) in
      stepsDone = true;
      if (totalSteps != nil) {
        results["steps"] = totalSteps;
      }

      if (stepsDone == true && distanceDone == true && caloriesDone == true) {
        return completion(results);
      }
    }

    self.getTotal(distance!, typeStr: HKManager.DISTANCE_TYPE_STR, unit: HKUnit.meter(), options: options) { (totalDistance, error) in
      distanceDone = true;
      if (totalDistance != nil) {
        results["distance"] = totalDistance;
      }
      if (stepsDone == true && distanceDone == true && caloriesDone == true) {
        return completion(results);
      }
    }

    self.getTotal(calories!, typeStr: HKManager.CALORIES_TYPE_STR, unit: HKUnit.kilocalorie(), options: options) { (totalCalories, error) in
      caloriesDone = true;
      if (totalCalories != nil) {
        results["calories"] = totalCalories;
      }
      if (stepsDone == true && distanceDone == true && caloriesDone == true) {
        return completion(results);
      }
    }
  }

I have also attached my implementation of the self.getTotal(…) function which is used in the above code. Point to note in this function, I switch to performing my HealthKit query in the background qos to ensure that these queries don’t run on the main thread. I think it might be the cause for the crash.


  func getTotal(_ type: HKQuantityType, typeStr: String, unit: HKUnit, options: Dictionary<String, Any>, completion: @escaping (Double?, Error?) -> Void) {
    guard (self.healthStore != nil) else {
      let error = NSError(domain: "Healthkit not initialized", code: 50, userInfo: [:]);
      return completion(nil, error);
    }

    var start: Date;

    if (options["startDate"] != nil) {
      start = self.strToDate(dateStr: options["startDate"] as! String);
    } else {
      let date = Date()
      let cal = Calendar(identifier: .gregorian)
      let midnight = cal.startOfDay(for: date);

      start = midnight;
    }

    var ignoreMin = false;
    if (options["ignoreMin"] != nil) {
      ignoreMin = options["ignoreMin"] as! Bool;
    }

    if (ignoreMin != true && start < self.minStartDate && self.minStartDate != nil) {
      start = self.minStartDate;
    }

    var end: Date = Date();
    if (options["endDate"] != nil) {
      end = self.strToDate(dateStr: options["endDate"] as! String);
    }

    var sources = options["sources"] as? [String];
    if (sources == nil || (sources?.capacity)! < 1) {
      sources = ["com.apple.health."];
    }

    DispatchQueue.global(qos: .background).async { [weak self] in

      // fetch sources
      self?.getSources(sampleTypeStr: typeStr, sampleType: type, sources: sources!) { (s, error) in

        if (s == nil || ((s?.capacity) ?? 0) < 1) {
          return completion(0.0, nil);
        }

        let sourcePredicate = HKQuery.predicateForObjects(from: s!);

        // todo: enter date patterns
        let datePredicate = HKQuery.predicateForSamples(withStart: start, end: end, options: []);

        //      predicate = [NSPredicate predicateWithFormat:@"metadata.%K != YES", HKMetadataKeyWasUserEntered];
        let manualPredicate = HKQuery.predicateForObjects(withMetadataKey: HKMetadataKeyWasUserEntered, operatorType: .notEqualTo, value: "YES");

        let compound = NSCompoundPredicate(andPredicateWithSubpredicates: [
          sourcePredicate,
          datePredicate,
          manualPredicate
        ]);


        let statOptions = HKStatisticsOptions.cumulativeSum;


        let query = HKStatisticsQuery.init(quantityType:type , quantitySamplePredicate: compound, options: statOptions, completionHandler: { (query, results, error) in

          if (error != nil) {
            return completion(nil, error);
          }

          var total = 0.0;

          // handle if results came back as nil, or sum came back as nil
          guard (results != nil && results?.sumQuantity() != nil) else {
            return completion(total, nil);
          }

          total = results?.sumQuantity()?.doubleValue(for: unit) ?? 0.0;

          return completion(total, nil);
        });

        // execute stats query for step counts by source
        self?.healthStore?.execute(query);
      }
   }
  }

I would really appreciate any form of help, or pointers. Thanks in advance.

Source: React natvie