Search code examples
iosswiftwatchkithealthkit

HealthKit authorization granted and working on simulator but not on actual device


I'm working on a workout app for the Apple Watch and ran into some problems using HealthKit on my actual watch.

Requesting authorization works on both the simulator and my device and shows as successful on every launch. Querying and reading the samples however fails on my device but not on the simulator.

After launching, with successful authorization, when it needs to query or save the workout it says "Authorization is not determined".

Both entitlements have set HealthKit to YES, HealthKit and Background capabilities are turned on, NSHealthShareUsageDescription and NSHealthUpdateUsageDescription keys are provided in the iOS Info.plist.

Authorization code

// Configure write values
    let writeTypes: Set<HKSampleType> = [.workoutType(),
                                         HKSampleType.quantityType(forIdentifier: .heartRate)!,
                                         HKSampleType.quantityType(forIdentifier: .activeEnergyBurned)!,
                                         HKSampleType.quantityType(forIdentifier: .stepCount)!,
                                         HKSampleType.quantityType(forIdentifier: .distanceCycling)!,
                                         HKSampleType.quantityType(forIdentifier: .distanceSwimming)!,
                                         HKSampleType.quantityType(forIdentifier: .distanceWalkingRunning)!,
                                         HKSampleType.quantityType(forIdentifier: .swimmingStrokeCount)!]
    // Configure read values
    let readTypes: Set<HKObjectType> = [.activitySummaryType(), .workoutType(),
                                        HKObjectType.quantityType(forIdentifier: .heartRate)!,
                                        HKObjectType.quantityType(forIdentifier: .activeEnergyBurned)!,
                                        HKObjectType.quantityType(forIdentifier: .stepCount)!,
                                        HKObjectType.quantityType(forIdentifier: .distanceCycling)!,
                                        HKObjectType.quantityType(forIdentifier: .distanceSwimming)!,
                                        HKObjectType.quantityType(forIdentifier: .distanceWalkingRunning)!,
                                        HKObjectType.quantityType(forIdentifier: .swimmingStrokeCount)!]

    // Create health store
    let healthStore = HKHealthStore()

    // Use it to request authorization for our types
    healthStore.requestAuthorization(toShare: writeTypes, read: readTypes) { (success, error) in
        if success {
            print("Success: authorization granted")
        } else {
            print("Error: \(error?.localizedDescription ?? "")")
        }
    }

Query code (Udemy course)

func startQuery(_ quantityTypeIdentifier: HKQuantityTypeIdentifier) {
    // We only want data points after our workout start date
    let datePredicate = HKQuery.predicateForSamples(withStart: workoutStartDate, end: nil, options: .strictStartDate)
    // And from our current device
    let devicePredicate = HKQuery.predicateForObjects(from: [HKDevice.local()])
    // Combine them
    let queryPredicate = NSCompoundPredicate(andPredicateWithSubpredicates: [datePredicate, devicePredicate])
    // Write code to receive results from our query
    let updateHandler: (HKAnchoredObjectQuery, [HKSample]?, [HKDeletedObject]?, HKQueryAnchor?, Error?) -> Void = { query, samples, deletedObjects, queryAnchor, error in

        //safely typecast to a quantity sample so we can read values
        guard let samples = samples as? [HKQuantitySample] else { return }

        //process the samples
        print("Start processing samples")
        self.process(samples, type: quantityTypeIdentifier)
    }

    // Create the query out of our type (e.g. heart rate), predicate and result handling code
    let quantityType = HKObjectType.quantityType(forIdentifier: quantityTypeIdentifier)!
    let query = HKAnchoredObjectQuery(type: quantityType, predicate: queryPredicate, anchor: nil, limit: HKObjectQueryNoLimit, resultsHandler: updateHandler)

    // Tell HealthKit to re-run the code every time new data is available
    query.updateHandler = updateHandler

    // Start the query running
    healthStore.execute(query)

    // Stach it away so we can stop it later
    activeDataQueries.append(query)
}

Solution

  • I put the authorization code in my ExtensionDelegate instead of the same file where I'm querying and it started working.

    Still strange that it worked on the simulator but not on an actual device before though.