feat(nutrition): 添加营养数据保存功能到HealthKit,包括蛋白质、脂肪和碳水化合物
This commit is contained in:
@@ -78,6 +78,19 @@ RCT_EXTERN_METHOD(getWaterIntakeFromHealthKit:(NSDictionary *)options
|
||||
resolver:(RCTPromiseResolveBlock)resolver
|
||||
rejecter:(RCTPromiseRejectBlock)rejecter)
|
||||
|
||||
// Nutrition Data Methods
|
||||
RCT_EXTERN_METHOD(saveProteinToHealthKit:(NSDictionary *)options
|
||||
resolver:(RCTPromiseResolveBlock)resolver
|
||||
rejecter:(RCTPromiseRejectBlock)rejecter)
|
||||
|
||||
RCT_EXTERN_METHOD(saveFatToHealthKit:(NSDictionary *)options
|
||||
resolver:(RCTPromiseResolveBlock)resolver
|
||||
rejecter:(RCTPromiseRejectBlock)rejecter)
|
||||
|
||||
RCT_EXTERN_METHOD(saveCarbohydratesToHealthKit:(NSDictionary *)options
|
||||
resolver:(RCTPromiseResolveBlock)resolver
|
||||
rejecter:(RCTPromiseRejectBlock)rejecter)
|
||||
|
||||
// Workout Data Methods
|
||||
RCT_EXTERN_METHOD(getRecentWorkouts:(NSDictionary *)options
|
||||
resolver:(RCTPromiseResolveBlock)resolver
|
||||
|
||||
@@ -88,11 +88,23 @@ class HealthKitManager: RCTEventEmitter {
|
||||
static var dietaryWater: HKQuantityType? {
|
||||
return HKObjectType.quantityType(forIdentifier: .dietaryWater)
|
||||
}
|
||||
static var dietaryProtein: HKQuantityType? {
|
||||
return HKObjectType.quantityType(forIdentifier: .dietaryProtein)
|
||||
}
|
||||
static var dietaryFatTotal: HKQuantityType? {
|
||||
return HKObjectType.quantityType(forIdentifier: .dietaryFatTotal)
|
||||
}
|
||||
static var dietaryCarbohydrates: HKQuantityType? {
|
||||
return HKObjectType.quantityType(forIdentifier: .dietaryCarbohydrates)
|
||||
}
|
||||
|
||||
static var all: Set<HKSampleType> {
|
||||
var types: Set<HKSampleType> = []
|
||||
if let bodyMass = bodyMass { types.insert(bodyMass) }
|
||||
if let dietaryWater = dietaryWater { types.insert(dietaryWater) }
|
||||
if let dietaryProtein = dietaryProtein { types.insert(dietaryProtein) }
|
||||
if let dietaryFatTotal = dietaryFatTotal { types.insert(dietaryFatTotal) }
|
||||
if let dietaryCarbohydrates = dietaryCarbohydrates { types.insert(dietaryCarbohydrates) }
|
||||
return types
|
||||
}
|
||||
}
|
||||
@@ -1669,6 +1681,194 @@ class HealthKitManager: RCTEventEmitter {
|
||||
healthStore.execute(query)
|
||||
}
|
||||
|
||||
// MARK: - Nutrition Data Methods
|
||||
|
||||
@objc
|
||||
func saveProteinToHealthKit(
|
||||
_ options: NSDictionary,
|
||||
resolver: @escaping RCTPromiseResolveBlock,
|
||||
rejecter: @escaping RCTPromiseRejectBlock
|
||||
) {
|
||||
guard HKHealthStore.isHealthDataAvailable() else {
|
||||
rejecter("HEALTHKIT_NOT_AVAILABLE", "HealthKit is not available on this device", nil)
|
||||
return
|
||||
}
|
||||
|
||||
// Parse parameters
|
||||
guard let amount = options["amount"] as? Double else {
|
||||
rejecter("INVALID_PARAMETERS", "Amount is required", nil)
|
||||
return
|
||||
}
|
||||
|
||||
let recordedAt: Date
|
||||
if let recordedAtString = options["recordedAt"] as? String,
|
||||
let date = parseDate(from: recordedAtString) {
|
||||
recordedAt = date
|
||||
} else {
|
||||
recordedAt = Date()
|
||||
}
|
||||
|
||||
guard let proteinType = WriteTypes.dietaryProtein else {
|
||||
rejecter("TYPE_NOT_AVAILABLE", "Protein type is not available", nil)
|
||||
return
|
||||
}
|
||||
|
||||
// Create quantity sample (protein in grams)
|
||||
let quantity = HKQuantity(unit: HKUnit.gram(), doubleValue: amount)
|
||||
let sample = HKQuantitySample(
|
||||
type: proteinType,
|
||||
quantity: quantity,
|
||||
start: recordedAt,
|
||||
end: recordedAt,
|
||||
metadata: nil
|
||||
)
|
||||
|
||||
// Save to HealthKit
|
||||
healthStore.save(sample) { [weak self] (success, error) in
|
||||
DispatchQueue.main.async {
|
||||
if let error = error {
|
||||
rejecter("SAVE_ERROR", "Failed to save protein: \(error.localizedDescription)", error)
|
||||
return
|
||||
}
|
||||
|
||||
if success {
|
||||
let result: [String: Any] = [
|
||||
"success": true,
|
||||
"amount": amount,
|
||||
"recordedAt": self?.dateToISOString(recordedAt) ?? ""
|
||||
]
|
||||
resolver(result)
|
||||
} else {
|
||||
rejecter("SAVE_FAILED", "Failed to save protein", nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@objc
|
||||
func saveFatToHealthKit(
|
||||
_ options: NSDictionary,
|
||||
resolver: @escaping RCTPromiseResolveBlock,
|
||||
rejecter: @escaping RCTPromiseRejectBlock
|
||||
) {
|
||||
guard HKHealthStore.isHealthDataAvailable() else {
|
||||
rejecter("HEALTHKIT_NOT_AVAILABLE", "HealthKit is not available on this device", nil)
|
||||
return
|
||||
}
|
||||
|
||||
// Parse parameters
|
||||
guard let amount = options["amount"] as? Double else {
|
||||
rejecter("INVALID_PARAMETERS", "Amount is required", nil)
|
||||
return
|
||||
}
|
||||
|
||||
let recordedAt: Date
|
||||
if let recordedAtString = options["recordedAt"] as? String,
|
||||
let date = parseDate(from: recordedAtString) {
|
||||
recordedAt = date
|
||||
} else {
|
||||
recordedAt = Date()
|
||||
}
|
||||
|
||||
guard let fatType = WriteTypes.dietaryFatTotal else {
|
||||
rejecter("TYPE_NOT_AVAILABLE", "Fat type is not available", nil)
|
||||
return
|
||||
}
|
||||
|
||||
// Create quantity sample (fat in grams)
|
||||
let quantity = HKQuantity(unit: HKUnit.gram(), doubleValue: amount)
|
||||
let sample = HKQuantitySample(
|
||||
type: fatType,
|
||||
quantity: quantity,
|
||||
start: recordedAt,
|
||||
end: recordedAt,
|
||||
metadata: nil
|
||||
)
|
||||
|
||||
// Save to HealthKit
|
||||
healthStore.save(sample) { [weak self] (success, error) in
|
||||
DispatchQueue.main.async {
|
||||
if let error = error {
|
||||
rejecter("SAVE_ERROR", "Failed to save fat: \(error.localizedDescription)", error)
|
||||
return
|
||||
}
|
||||
|
||||
if success {
|
||||
let result: [String: Any] = [
|
||||
"success": true,
|
||||
"amount": amount,
|
||||
"recordedAt": self?.dateToISOString(recordedAt) ?? ""
|
||||
]
|
||||
resolver(result)
|
||||
} else {
|
||||
rejecter("SAVE_FAILED", "Failed to save fat", nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@objc
|
||||
func saveCarbohydratesToHealthKit(
|
||||
_ options: NSDictionary,
|
||||
resolver: @escaping RCTPromiseResolveBlock,
|
||||
rejecter: @escaping RCTPromiseRejectBlock
|
||||
) {
|
||||
guard HKHealthStore.isHealthDataAvailable() else {
|
||||
rejecter("HEALTHKIT_NOT_AVAILABLE", "HealthKit is not available on this device", nil)
|
||||
return
|
||||
}
|
||||
|
||||
// Parse parameters
|
||||
guard let amount = options["amount"] as? Double else {
|
||||
rejecter("INVALID_PARAMETERS", "Amount is required", nil)
|
||||
return
|
||||
}
|
||||
|
||||
let recordedAt: Date
|
||||
if let recordedAtString = options["recordedAt"] as? String,
|
||||
let date = parseDate(from: recordedAtString) {
|
||||
recordedAt = date
|
||||
} else {
|
||||
recordedAt = Date()
|
||||
}
|
||||
|
||||
guard let carbohydratesType = WriteTypes.dietaryCarbohydrates else {
|
||||
rejecter("TYPE_NOT_AVAILABLE", "Carbohydrates type is not available", nil)
|
||||
return
|
||||
}
|
||||
|
||||
// Create quantity sample (carbohydrates in grams)
|
||||
let quantity = HKQuantity(unit: HKUnit.gram(), doubleValue: amount)
|
||||
let sample = HKQuantitySample(
|
||||
type: carbohydratesType,
|
||||
quantity: quantity,
|
||||
start: recordedAt,
|
||||
end: recordedAt,
|
||||
metadata: nil
|
||||
)
|
||||
|
||||
// Save to HealthKit
|
||||
healthStore.save(sample) { [weak self] (success, error) in
|
||||
DispatchQueue.main.async {
|
||||
if let error = error {
|
||||
rejecter("SAVE_ERROR", "Failed to save carbohydrates: \(error.localizedDescription)", error)
|
||||
return
|
||||
}
|
||||
|
||||
if success {
|
||||
let result: [String: Any] = [
|
||||
"success": true,
|
||||
"amount": amount,
|
||||
"recordedAt": self?.dateToISOString(recordedAt) ?? ""
|
||||
]
|
||||
resolver(result)
|
||||
} else {
|
||||
rejecter("SAVE_FAILED", "Failed to save carbohydrates", nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Workout Data Methods
|
||||
|
||||
@objc
|
||||
|
||||
Reference in New Issue
Block a user