Мой swift-код Healthkit продолжает работать в фоновом режиме, и я не могу понять это на всю жизнь. Я довольно новичок в Swift, поэтому, возможно, я делаю фундаментальную ошибку в своей реализации.
Особенности cra sh:
- Кажется, это происходит только в производстве. (наша внутренняя тестовая программа содержит только 10 устройств (так что, возможно, это совпадение, что ее там не обнаружат).
- Происходит в iOS версиях - 10,11,12,13
- Происходит для очень небольшого набора пользователей (1,5% активной аудитории), но очень часто для этих же пользователей.
Пожалуйста, найдите журнал cra sh из моей учетной записи Crashlytics ниже .
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
com.apple.main-thread
0 libsystem_kernel.dylib 0x19c960634 mach_msg_trap + 8
1 libsystem_kernel.dylib 0x19c95faa0 mach_msg + 72
2 CoreFoundation 0x19cb08288 __CFRunLoopServiceMachPort + 216
3 CoreFoundation 0x19cb033a8 __CFRunLoopRun + 1444
4 CoreFoundation 0x19cb02adc CFRunLoopRunSpecific + 464
5 GraphicsServices 0x1a6aa3328 GSEventRunModal + 104
6 UIKitCore 0x1a0c1063c UIApplicationMain + 1936
7 stepapp 0x100edf330 main + 14 (main.m:14)
8 libdyld.dylib 0x19c98c360 start + 4
com.apple.uikit.eventfetch-thread
0 libsystem_kernel.dylib 0x19c960634 mach_msg_trap + 8
1 libsystem_kernel.dylib 0x19c95faa0 mach_msg + 72
2 CoreFoundation 0x19cb08288 __CFRunLoopServiceMachPort + 216
3 CoreFoundation 0x19cb033a8 __CFRunLoopRun + 1444
4 CoreFoundation 0x19cb02adc CFRunLoopRunSpecific + 464
5 Foundation 0x19ce42784 -[NSRunLoop(NSRunLoop) runMode:beforeDate:] + 228
6 Foundation 0x19ce42664 -[NSRunLoop(NSRunLoop) runUntilDate:] + 88
7 UIKitCore 0x1a0ca8e80 -[UIEventFetcher threadMain] + 152
8 Foundation 0x19cf7309c __NSThread__start__ + 848
9 libsystem_pthread.dylib 0x19c8a5d8c _pthread_start + 156
10 libsystem_pthread.dylib 0x19c8a976c thread_start + 8
Я приложил свою реализацию ниже, которая содержит строку, упомянутую в журналах cra sh
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);
}
}
}
Я также приложил свою реализацию self.getTotal ( ...), которая используется в приведенном выше коде. Обратите внимание, что в этой функции я переключаюсь на выполнение своего запроса HealthKit в фоновом режиме qos, чтобы убедиться, что эти запросы не выполняются в главном потоке. причина крэ sh.
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);
}
}
}
Буду очень признателен за любую помощь или указатели. Заранее спасибо.