Код: Выделить всё
Cannot find 'HKWorkoutPlan' in scope
Код: Выделить всё
Cannot find 'HKWorkoutComposition' in scope
Код: Выделить всё
Cannot find type 'HKWorkoutGoal' in scope
Код: Выделить всё
Cannot find type 'HKWorkoutComposition' in scope
//
// HealthKitManager.swift
// AppHere
//
import Foundation
import HealthKit
class HealthKitManager {
// MARK: - Properties
// Create a singleton instance so the whole app can share it.
static let shared = HealthKitManager()
let healthStore = HKHealthStore()
// Private init to enforce the singleton pattern.
private init() {}
// MARK: - Authorization
func requestAuthorization(completion: @escaping (Bool, Error?) -> Void) {
// First, gotta make sure HealthKit is actually available on this device.
guard HKHealthStore.isHealthDataAvailable() else {
completion(false, HealthKitError.notAvailableOnDevice)
return
}
// These are the data types I'll be writing to HealthKit.
let typesToShare: Set = [
HKObjectType.workoutType()
]
// I don't need to read any data right now, but I'm requesting permission anyway
// so I don't have to ask the user again later if I add read features.
let typesToRead: Set = [
HKObjectType.workoutType()
]
// Now, actually ask the user for permission.
healthStore.requestAuthorization(toShare: typesToShare, read: typesToRead) { success, error in
// This completion can come back on a background thread, so I need to make
// sure I dispatch to the main queue before calling the completion handler.
DispatchQueue.main.async {
completion(success, error)
}
}
}
// MARK: - Workout Plan Management
// Even though my deployment target is iOS 17, using @available makes the code's
// requirements clearer and prevents accidental use if the target ever changes.
@available(iOS 17.0, *)
func saveWorkoutPlan(_ workout: StructuredWorkout, completion: @escaping (Bool, Error?) -> Void) {
do {
// Set up the basic configuration for this workout.
let workoutConfiguration = HKWorkoutConfiguration()
workoutConfiguration.activityType = workout.discipline
workoutConfiguration.locationType = .outdoor // Assuming outdoor for now.
var finalBlocks: [HKWorkoutComposition.Block] = []
var blockBuffer: [HKWorkoutComposition.Block] = []
for step in workout.steps {
if step.type == .repeatStep {
// If this is a repeat step, I need to wrap everything in the buffer
// into a repeating block.
guard !blockBuffer.isEmpty, let repeatCount = step.repeatCount else { continue }
// Create a composition from the buffered steps that need to be repeated.
let repeatComposition = try HKWorkoutComposition(blocks: blockBuffer)
let repeatBlock = try HKWorkoutComposition.Block(composition: repeatComposition, repetitions: repeatCount)
finalBlocks.append(repeatBlock)
// Clear out the buffer so it's ready for the next set of steps.
blockBuffer.removeAll()
} else {
// For a regular step, just create a single block and add it to the buffer.
let goal = workoutGoal(for: step)
let block = try HKWorkoutComposition.Block(activity: workoutConfiguration, goal: goal)
blockBuffer.append(block)
}
}
// After the loop, I need to add any steps left in the buffer that weren't
// part of a final repeat block.
if !blockBuffer.isEmpty {
finalBlocks.append(contentsOf: blockBuffer)
}
// Build the final composition from all the blocks I've created.
let composition = try HKWorkoutComposition(blocks: finalBlocks)
let plan = HKWorkoutPlan(name: workout.name, composition: composition, date: workout.date)
healthStore.save(plan) { success, error in
if success {
print("
}
// Make sure to call the completion handler on the main thread.
DispatchQueue.main.async {
completion(success, error)
}
}
} catch {
// Catch any errors that happen during the block/composition creation.
print("
completion(false, error)
}
}
// Helper function to turn my WorkoutStep into a HealthKit goal.
@available(iOS 17.0, *)
private func workoutGoal(for step: WorkoutStep) -> HKWorkoutGoal {
switch step.target {
case .open:
return .init(duration: step.duration)
case .power(let low, let high):
let lowPower = HKQuantity(unit: .watt(), doubleValue: low)
let highPower = HKQuantity(unit: .watt(), doubleValue: high)
return .init(powerRangeFrom: lowPower, to: highPower, for: step.duration)
case .pace(let low, let high):
// Important: HealthKit expects speed (m/s), not pace (min/mile).
// I'm handling the conversion before this function is ever called.
let lowSpeed = HKQuantity(unit: .meter().unitDivided(by: .second()), doubleValue: low)
let highSpeed = HKQuantity(unit: .meter().unitDivided(by: .second()), doubleValue: high)
return .init(speedRangeFrom: lowSpeed, to: highSpeed, for: step.duration)
case .heartRate(let low, let high):
let bpmUnit = HKUnit.count().unitDivided(by: .minute())
let lowHR = HKQuantity(unit: bpmUnit, doubleValue: low)
let highHR = HKQuantity(unit: bpmUnit, doubleValue: high)
return .init(heartRateRangeFrom: lowHR, to: highHR, for: step.duration)
}
}
}
// My own custom errors to make debugging HealthKit interactions more specific.
enum HealthKitError: Error {
case notAvailableOnDevice
case authorizationFailed
}
< /code>
Я также заметил, что App.entitlements Целевое членство является пустым, но я не могу добавить цели из -за того, что они сероалировали. Я могу установить флажок, но как только я нажму сохранение, он не применяется. < /P>
Любая помощь ценится. Рад предоставить больше контекста или информации, если это необходимо. < /P>
Измененные минимальные минимальные развертывания на 17,6 и 18,6 < /p>
Подтвержденный импорт HealthKit присутствует в верхней части файла.
Код обернут в @available (ios 17.0, *). «Подписание и возможности», была добавлена возможность HealthKit.>
Подробнее здесь: https://stackoverflow.com/questions/796 ... tion-using