diff --git a/README.md b/README.md
index 67793be..114e10b 100644
--- a/README.md
+++ b/README.md
@@ -108,7 +108,7 @@ Health Connect ships in the platform on Android 14+. On Android 8–13, users in
1. **Minimum SDK**: `26` (Android 8.0). The module's `build.gradle` defaults match.
-2. **Declare permissions** in your host app's `AndroidManifest.xml` (or via `app.config.ts` if you use Expo prebuild). The library's own manifest declares the same set so it merges naturally.
+2. **Declare permissions** in your host app's `AndroidManifest.xml` (or via `app.config.ts` if you use Expo prebuild). The library's own manifest declares its default set for backward compatibility, so remove any defaults your app does not use from the final merged manifest.
```xml
@@ -256,17 +256,42 @@ export default function HealthScreen() {
## 📚 API Reference
-### `requestAuthorization(): Promise`
+### `requestAuthorization(readTypes?: string[]): Promise`
-Requests authorization to access HealthKit data.
+Requests authorization to read health data. When `readTypes` is omitted, the module requests its default set for backward compatibility. Pass only the types your app reads to keep the system prompt scoped to the features you use.
**Returns:** `Promise` - `true` if authorized, `false` otherwise
**Example:**
```typescript
-const authorized = await HealthKitModule.requestAuthorization();
+import {
+ HealthKitCategoryType,
+ HealthKitModule,
+ HealthKitQuantityType,
+ HealthKitWorkoutType,
+} from 'react-native-nitro-healthkit';
+
+const authorized = await HealthKitModule.requestAuthorization([
+ HealthKitQuantityType.STEPS,
+ HealthKitQuantityType.HEART_RATE,
+ HealthKitCategoryType.SLEEP_ANALYSIS,
+ HealthKitWorkoutType.WORKOUT,
+]);
+```
+
+On Android, declare the matching `READ_*` permissions in the host app manifest. The optional scope controls which permissions the module checks. Because the library manifest supplies its default set for backward compatibility, use Android manifest merger removal rules for every unneeded permission:
+
+```xml
+
+
+
```
+On iOS, HealthKit does not reveal whether a user granted or denied read access to an individual type. `checkAuthorizationStatus()` therefore reports `sharingAuthorized` when the system no longer needs to show an authorization request for that type, and `notDetermined` when a request is still needed. This includes workout data via `HealthKitWorkoutType.WORKOUT`.
+
---
### `getSteps(startDate: Date, endDate: Date): Promise`
diff --git a/packages/__tests__/HealthKitModule.test.ts b/packages/__tests__/HealthKitModule.test.ts
index 8625dad..93fe973 100644
--- a/packages/__tests__/HealthKitModule.test.ts
+++ b/packages/__tests__/HealthKitModule.test.ts
@@ -1,4 +1,9 @@
-import { HealthKitModule, HealthKitQuantityType, HealthKitCategoryType } from '../src/index';
+import {
+ HealthKitCategoryType,
+ HealthKitModule,
+ HealthKitQuantityType,
+ HealthKitWorkoutType,
+} from '../src/index';
import { describe, it, expect, beforeAll } from '@jest/globals';
describe('HealthKitModule', () => {
@@ -20,6 +25,17 @@ describe('HealthKitModule', () => {
const authorized = await HealthKitModule.requestAuthorization();
expect(typeof authorized).toBe('boolean');
});
+
+ it('should request authorization for a scoped set of types', async () => {
+ const authorized = await HealthKitModule.requestAuthorization([
+ HealthKitQuantityType.STEPS,
+ HealthKitCategoryType.SLEEP_ANALYSIS,
+ HealthKitWorkoutType.WORKOUT,
+ ]);
+
+ expect(typeof authorized).toBe('boolean');
+ expect(HealthKitWorkoutType.WORKOUT).toBe('HKWorkoutTypeIdentifier');
+ });
});
describe('Legacy Methods', () => {
diff --git a/packages/android/src/main/kotlin/io/github/n0ku/nitrohealthkit/HealthKitModule.kt b/packages/android/src/main/kotlin/io/github/n0ku/nitrohealthkit/HealthKitModule.kt
index 1f769ab..49d22f5 100644
--- a/packages/android/src/main/kotlin/io/github/n0ku/nitrohealthkit/HealthKitModule.kt
+++ b/packages/android/src/main/kotlin/io/github/n0ku/nitrohealthkit/HealthKitModule.kt
@@ -37,6 +37,7 @@ import com.margelo.nitro.healthkit.WorkoutDataPoint
import io.github.n0ku.nitrohealthkit.auth.SecureCredentialsStore
import io.github.n0ku.nitrohealthkit.cache.CacheManager
import io.github.n0ku.nitrohealthkit.mappers.CategoryMapper
+import io.github.n0ku.nitrohealthkit.mappers.PermissionMapper
import io.github.n0ku.nitrohealthkit.mappers.QuantityMapper
import io.github.n0ku.nitrohealthkit.mappers.WorkoutMapper
import io.github.n0ku.nitrohealthkit.observers.ChangesObserver
@@ -84,8 +85,8 @@ class HealthKitModule : HybridHealthKitSpec() {
}
/**
- * Reports whether the default Health Connect permissions are
- * already granted. When at least one required permission is
+ * Reports whether the selected (or default) Health Connect permissions
+ * are already granted. When at least one required permission is
* missing, also launches Health Connect's "Manage permissions"
* screen so the user can grant access without having to navigate
* there manually — this is what callers expect from "request"
@@ -101,11 +102,11 @@ class HealthKitModule : HybridHealthKitSpec() {
* the fetch on every `AppState` `active` transition, so when the
* user returns from HC the data flows in on its own.
*/
- override fun requestAuthorization(): Promise = Promise.async {
+ override fun requestAuthorization(readTypes: Array?): Promise = Promise.async {
if (HealthConnectClient.getSdkStatus(appContext) != HealthConnectClient.SDK_AVAILABLE) {
return@async false
}
- val required = defaultPermissions()
+ val required = readTypes?.let(PermissionMapper::readPermissionsFor) ?: defaultPermissions()
val granted = client.permissionController.getGrantedPermissions()
if (granted.containsAll(required)) {
return@async true
@@ -127,9 +128,7 @@ class HealthKitModule : HybridHealthKitSpec() {
}
override fun checkAuthorizationStatus(type: String): Promise = Promise.async {
- val quantityPerm = QuantityMapper.readPermissionFor(type)
- val categoryPerm = CategoryMapper.readPermissionFor(type)
- val permission = quantityPerm ?: categoryPerm ?: return@async NOT_DETERMINED
+ val permission = PermissionMapper.readPermissionFor(type) ?: return@async NOT_DETERMINED
val granted = client.permissionController.getGrantedPermissions()
if (granted.contains(permission)) AUTHORIZED else DENIED
}
diff --git a/packages/android/src/main/kotlin/io/github/n0ku/nitrohealthkit/mappers/PermissionMapper.kt b/packages/android/src/main/kotlin/io/github/n0ku/nitrohealthkit/mappers/PermissionMapper.kt
new file mode 100644
index 0000000..37ca188
--- /dev/null
+++ b/packages/android/src/main/kotlin/io/github/n0ku/nitrohealthkit/mappers/PermissionMapper.kt
@@ -0,0 +1,21 @@
+package io.github.n0ku.nitrohealthkit.mappers
+
+import androidx.health.connect.client.permission.HealthPermission
+import androidx.health.connect.client.records.ExerciseSessionRecord
+
+internal object PermissionMapper {
+
+ const val WORKOUT_TYPE = "HKWorkoutTypeIdentifier"
+
+ fun readPermissionFor(type: String): String? =
+ QuantityMapper.readPermissionFor(type)
+ ?: CategoryMapper.readPermissionFor(type)
+ ?: if (type == WORKOUT_TYPE) {
+ HealthPermission.getReadPermission(ExerciseSessionRecord::class)
+ } else {
+ null
+ }
+
+ fun readPermissionsFor(types: Array): Set =
+ types.mapNotNull(::readPermissionFor).toSet()
+}
diff --git a/packages/android/src/test/kotlin/io/github/n0ku/nitrohealthkit/mappers/PermissionMapperTest.kt b/packages/android/src/test/kotlin/io/github/n0ku/nitrohealthkit/mappers/PermissionMapperTest.kt
new file mode 100644
index 0000000..ca6ecd9
--- /dev/null
+++ b/packages/android/src/test/kotlin/io/github/n0ku/nitrohealthkit/mappers/PermissionMapperTest.kt
@@ -0,0 +1,53 @@
+package io.github.n0ku.nitrohealthkit.mappers
+
+import androidx.health.connect.client.permission.HealthPermission
+import androidx.health.connect.client.records.ExerciseSessionRecord
+import androidx.health.connect.client.records.SleepSessionRecord
+import androidx.health.connect.client.records.StepsRecord
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertNull
+import org.junit.Test
+
+class PermissionMapperTest {
+
+ @Test
+ fun `readPermissionFor resolves quantity category and workout types`() {
+ assertEquals(
+ HealthPermission.getReadPermission(StepsRecord::class),
+ PermissionMapper.readPermissionFor(QuantityMapper.QT_STEPS),
+ )
+ assertEquals(
+ HealthPermission.getReadPermission(SleepSessionRecord::class),
+ PermissionMapper.readPermissionFor(CategoryMapper.CT_SLEEP_ANALYSIS),
+ )
+ assertEquals(
+ HealthPermission.getReadPermission(ExerciseSessionRecord::class),
+ PermissionMapper.readPermissionFor(PermissionMapper.WORKOUT_TYPE),
+ )
+ }
+
+ @Test
+ fun `readPermissionFor returns null for unsupported types`() {
+ assertNull(PermissionMapper.readPermissionFor("HKQuantityTypeIdentifierAppleExerciseTime"))
+ }
+
+ @Test
+ fun `readPermissionsFor ignores unsupported types and removes duplicates`() {
+ val permissions = PermissionMapper.readPermissionsFor(
+ arrayOf(
+ QuantityMapper.QT_STEPS,
+ QuantityMapper.QT_STEPS,
+ PermissionMapper.WORKOUT_TYPE,
+ "unsupported",
+ ),
+ )
+
+ assertEquals(
+ setOf(
+ HealthPermission.getReadPermission(StepsRecord::class),
+ HealthPermission.getReadPermission(ExerciseSessionRecord::class),
+ ),
+ permissions,
+ )
+ }
+}
diff --git a/packages/ios/HealthKitModule.swift b/packages/ios/HealthKitModule.swift
index 677ddcb..287a65e 100644
--- a/packages/ios/HealthKitModule.swift
+++ b/packages/ios/HealthKitModule.swift
@@ -10,8 +10,8 @@ public class HealthKitModule: HybridHealthKitSpec, @unchecked Sendable {
// Default cache configuration
private let defaultCacheTTL: TimeInterval = 60 // 1 minute
- // Data types to authorize (extended)
- private let readTypes: Set = {
+ // Data types requested when callers do not provide an explicit scope.
+ private let defaultReadTypes: Set = {
var types: Set = []
// Workout type
@@ -49,12 +49,30 @@ public class HealthKitModule: HybridHealthKitSpec, @unchecked Sendable {
return types
}()
+
+ private static func objectType(for identifier: String) -> HKObjectType? {
+ if let quantityIdentifier = HKQuantityTypeIdentifier.from(identifier) {
+ return HKObjectType.quantityType(forIdentifier: quantityIdentifier)
+ }
+ if let categoryIdentifier = HKCategoryTypeIdentifier.from(identifier) {
+ return HKObjectType.categoryType(forIdentifier: categoryIdentifier)
+ }
+ if identifier == "HKWorkoutTypeIdentifier" {
+ return HKObjectType.workoutType()
+ }
+ return nil
+ }
+
+ private func resolveReadTypes(_ identifiers: [String]?) -> Set {
+ guard let identifiers else { return defaultReadTypes }
+ return Set(identifiers.compactMap(Self.objectType(for:)))
+ }
public override init() {
super.init()
}
- public func requestAuthorization() throws -> Promise {
+ public func requestAuthorization(readTypes: [String]?) throws -> Promise {
return Promise.async { [weak self] in
guard let self = self else { return false }
@@ -65,9 +83,11 @@ public class HealthKitModule: HybridHealthKitSpec, @unchecked Sendable {
guard HKHealthStore.isHealthDataAvailable() else {
throw NSError(domain: "HealthKit", code: 1, userInfo: [NSLocalizedDescriptionKey: "HealthKit not available"])
}
+
+ let requestedReadTypes = self.resolveReadTypes(readTypes)
return try await withCheckedThrowingContinuation { continuation in
- self.healthStore.requestAuthorization(toShare: [], read: self.readTypes) { success, error in
+ self.healthStore.requestAuthorization(toShare: [], read: requestedReadTypes) { success, error in
if let error = error {
continuation.resume(throwing: error)
} else {
@@ -657,32 +677,26 @@ public class HealthKitModule: HybridHealthKitSpec, @unchecked Sendable {
return Promise.async { [weak self] in
guard let self = self else { return "notDetermined" }
- var objectType: HKObjectType?
-
- // Essayer d'abord comme quantity type
- if let identifier = HKQuantityTypeIdentifier.from(type) {
- objectType = HKObjectType.quantityType(forIdentifier: identifier)
- }
- // Sinon essayer comme category type
- else if let identifier = HKCategoryTypeIdentifier.from(type) {
- objectType = HKObjectType.categoryType(forIdentifier: identifier)
- }
-
- guard let type = objectType else {
+ guard let objectType = Self.objectType(for: type) else {
throw NSError(domain: "HealthKit", code: 2, userInfo: [NSLocalizedDescriptionKey: "Invalid type: \(type)"])
}
-
- let status = self.healthStore.authorizationStatus(for: type)
-
- switch status {
- case .notDetermined:
- return "notDetermined"
- case .sharingDenied:
- return "sharingDenied"
- case .sharingAuthorized:
- return "sharingAuthorized"
- @unknown default:
- return "notDetermined"
+
+ return try await withCheckedThrowingContinuation { continuation in
+ self.healthStore.getRequestStatusForAuthorization(toShare: [], read: [objectType]) { status, error in
+ if let error = error {
+ continuation.resume(throwing: error)
+ return
+ }
+
+ switch status {
+ case .unnecessary:
+ continuation.resume(returning: "sharingAuthorized")
+ case .shouldRequest, .unknown:
+ continuation.resume(returning: "notDetermined")
+ @unknown default:
+ continuation.resume(returning: "notDetermined")
+ }
+ }
}
}
}
@@ -818,4 +832,4 @@ public class HealthKitModule: HybridHealthKitSpec, @unchecked Sendable {
}
}
-}
\ No newline at end of file
+}
diff --git a/packages/nitrogen/generated/android/c++/JHybridHealthKitSpec.cpp b/packages/nitrogen/generated/android/c++/JHybridHealthKitSpec.cpp
index e625e7e..6e67786 100644
--- a/packages/nitrogen/generated/android/c++/JHybridHealthKitSpec.cpp
+++ b/packages/nitrogen/generated/android/c++/JHybridHealthKitSpec.cpp
@@ -65,9 +65,17 @@ namespace margelo::nitro::healthkit {
// Methods
- std::shared_ptr> JHybridHealthKitSpec::requestAuthorization() {
- static const auto method = javaClassStatic()->getMethod()>("requestAuthorization");
- auto __result = method(_javaPart);
+ std::shared_ptr> JHybridHealthKitSpec::requestAuthorization(const std::optional>& readTypes) {
+ static const auto method = javaClassStatic()->getMethod(jni::alias_ref> /* readTypes */)>("requestAuthorization");
+ auto __result = method(_javaPart, readTypes.has_value() ? [&]() {
+ size_t __size = readTypes.value().size();
+ jni::local_ref> __array = jni::JArrayClass::newArray(__size);
+ for (size_t __i = 0; __i < __size; __i++) {
+ const auto& __element = readTypes.value()[__i];
+ __array->setElement(__i, *jni::make_jstring(__element));
+ }
+ return __array;
+ }() : nullptr);
return [&]() {
auto __promise = Promise::create();
__result->cthis()->addOnResolvedListener([=](const jni::alias_ref& __boxedResult) {
diff --git a/packages/nitrogen/generated/android/c++/JHybridHealthKitSpec.hpp b/packages/nitrogen/generated/android/c++/JHybridHealthKitSpec.hpp
index e4ff896..9166a40 100644
--- a/packages/nitrogen/generated/android/c++/JHybridHealthKitSpec.hpp
+++ b/packages/nitrogen/generated/android/c++/JHybridHealthKitSpec.hpp
@@ -53,7 +53,7 @@ namespace margelo::nitro::healthkit {
public:
// Methods
- std::shared_ptr> requestAuthorization() override;
+ std::shared_ptr> requestAuthorization(const std::optional>& readTypes) override;
std::shared_ptr>> getQuantityData(const std::string& type, std::chrono::system_clock::time_point startDate, std::chrono::system_clock::time_point endDate, const std::optional& aggregationType, std::optional useCache, std::optional cacheTTL) override;
std::shared_ptr> getAggregatedQuantity(const std::string& type, std::chrono::system_clock::time_point startDate, std::chrono::system_clock::time_point endDate, const std::string& aggregationType, std::optional useCache, std::optional cacheTTL) override;
std::shared_ptr>> getCategoryData(const std::string& type, std::chrono::system_clock::time_point startDate, std::chrono::system_clock::time_point endDate, std::optional useCache, std::optional cacheTTL) override;
diff --git a/packages/nitrogen/generated/android/kotlin/com/margelo/nitro/healthkit/HybridHealthKitSpec.kt b/packages/nitrogen/generated/android/kotlin/com/margelo/nitro/healthkit/HybridHealthKitSpec.kt
index 57f5612..43aa6a3 100644
--- a/packages/nitrogen/generated/android/kotlin/com/margelo/nitro/healthkit/HybridHealthKitSpec.kt
+++ b/packages/nitrogen/generated/android/kotlin/com/margelo/nitro/healthkit/HybridHealthKitSpec.kt
@@ -42,7 +42,7 @@ abstract class HybridHealthKitSpec: HybridObject() {
// Methods
@DoNotStrip
@Keep
- abstract fun requestAuthorization(): Promise
+ abstract fun requestAuthorization(readTypes: Array?): Promise
@DoNotStrip
@Keep
diff --git a/packages/nitrogen/generated/ios/NitroHealthkit-Swift-Cxx-Bridge.hpp b/packages/nitrogen/generated/ios/NitroHealthkit-Swift-Cxx-Bridge.hpp
index 91f1eec..fc45a6b 100644
--- a/packages/nitrogen/generated/ios/NitroHealthkit-Swift-Cxx-Bridge.hpp
+++ b/packages/nitrogen/generated/ios/NitroHealthkit-Swift-Cxx-Bridge.hpp
@@ -104,6 +104,32 @@ namespace margelo::nitro::healthkit::bridge::swift {
return Func_void_std__exception_ptr_Wrapper(std::move(value));
}
+ // pragma MARK: std::vector
+ /**
+ * Specialized version of `std::vector`.
+ */
+ using std__vector_std__string_ = std::vector;
+ inline std::vector create_std__vector_std__string_(size_t size) noexcept {
+ std::vector vector;
+ vector.reserve(size);
+ return vector;
+ }
+
+ // pragma MARK: std::optional>
+ /**
+ * Specialized version of `std::optional>`.
+ */
+ using std__optional_std__vector_std__string__ = std::optional>;
+ inline std::optional> create_std__optional_std__vector_std__string__(const std::vector& value) noexcept {
+ return std::optional>(value);
+ }
+ inline bool has_value_std__optional_std__vector_std__string__(const std::optional>& optional) noexcept {
+ return optional.has_value();
+ }
+ inline std::vector get_std__optional_std__vector_std__string__(const std::optional>& optional) noexcept {
+ return *optional;
+ }
+
// pragma MARK: std::unordered_map
/**
* Specialized version of `std::unordered_map`.
@@ -475,17 +501,6 @@ namespace margelo::nitro::healthkit::bridge::swift {
return Func_void_std__vector_WorkoutDataPoint__Wrapper(std::move(value));
}
- // pragma MARK: std::vector
- /**
- * Specialized version of `std::vector`.
- */
- using std__vector_std__string_ = std::vector;
- inline std::vector create_std__vector_std__string_(size_t size) noexcept {
- std::vector vector;
- vector.reserve(size);
- return vector;
- }
-
// pragma MARK: std::shared_ptr
/**
* Specialized version of `std::shared_ptr`.
diff --git a/packages/nitrogen/generated/ios/c++/HybridHealthKitSpecSwift.hpp b/packages/nitrogen/generated/ios/c++/HybridHealthKitSpecSwift.hpp
index 853da91..4d00ef2 100644
--- a/packages/nitrogen/generated/ios/c++/HybridHealthKitSpecSwift.hpp
+++ b/packages/nitrogen/generated/ios/c++/HybridHealthKitSpecSwift.hpp
@@ -24,12 +24,12 @@ namespace margelo::nitro::healthkit { struct WorkoutDataPoint; }
namespace margelo::nitro::healthkit { struct BackgroundSyncConfig; }
#include
-#include "QuantityDataPoint.hpp"
-#include
#include
+#include
+#include
+#include "QuantityDataPoint.hpp"
#include
#include
-#include
#include "CategoryDataPoint.hpp"
#include "HealthData.hpp"
#include "WorkoutDataPoint.hpp"
@@ -77,8 +77,8 @@ namespace margelo::nitro::healthkit {
public:
// Methods
- inline std::shared_ptr> requestAuthorization() override {
- auto __result = _swiftPart.requestAuthorization();
+ inline std::shared_ptr> requestAuthorization(const std::optional>& readTypes) override {
+ auto __result = _swiftPart.requestAuthorization(readTypes);
if (__result.hasError()) [[unlikely]] {
std::rethrow_exception(__result.error());
}
diff --git a/packages/nitrogen/generated/ios/swift/HybridHealthKitSpec.swift b/packages/nitrogen/generated/ios/swift/HybridHealthKitSpec.swift
index b54126f..043fe39 100644
--- a/packages/nitrogen/generated/ios/swift/HybridHealthKitSpec.swift
+++ b/packages/nitrogen/generated/ios/swift/HybridHealthKitSpec.swift
@@ -14,7 +14,7 @@ public protocol HybridHealthKitSpec_protocol: HybridObject {
// Methods
- func requestAuthorization() throws -> Promise
+ func requestAuthorization(readTypes: [String]?) throws -> Promise
func getQuantityData(type: String, startDate: Date, endDate: Date, aggregationType: String?, useCache: Bool?, cacheTTL: Double?) throws -> Promise<[QuantityDataPoint]>
func getAggregatedQuantity(type: String, startDate: Date, endDate: Date, aggregationType: String, useCache: Bool?, cacheTTL: Double?) throws -> Promise
func getCategoryData(type: String, startDate: Date, endDate: Date, useCache: Bool?, cacheTTL: Double?) throws -> Promise<[CategoryDataPoint]>
diff --git a/packages/nitrogen/generated/ios/swift/HybridHealthKitSpec_cxx.swift b/packages/nitrogen/generated/ios/swift/HybridHealthKitSpec_cxx.swift
index 9583c9f..be244f0 100644
--- a/packages/nitrogen/generated/ios/swift/HybridHealthKitSpec_cxx.swift
+++ b/packages/nitrogen/generated/ios/swift/HybridHealthKitSpec_cxx.swift
@@ -110,9 +110,16 @@ open class HybridHealthKitSpec_cxx {
// Methods
@inline(__always)
- public final func requestAuthorization() -> bridge.Result_std__shared_ptr_Promise_bool___ {
+ public final func requestAuthorization(readTypes: bridge.std__optional_std__vector_std__string__) -> bridge.Result_std__shared_ptr_Promise_bool___ {
do {
- let __result = try self.__implementation.requestAuthorization()
+ let __result = try self.__implementation.requestAuthorization(readTypes: { () -> [String]? in
+ if bridge.has_value_std__optional_std__vector_std__string__(readTypes) {
+ let __unwrapped = bridge.get_std__optional_std__vector_std__string__(readTypes)
+ return __unwrapped.map({ __item in String(__item) })
+ } else {
+ return nil
+ }
+ }())
let __resultCpp = { () -> bridge.std__shared_ptr_Promise_bool__ in
let __promise = bridge.create_std__shared_ptr_Promise_bool__()
let __promiseHolder = bridge.wrap_std__shared_ptr_Promise_bool__(__promise)
diff --git a/packages/nitrogen/generated/shared/c++/HybridHealthKitSpec.hpp b/packages/nitrogen/generated/shared/c++/HybridHealthKitSpec.hpp
index 556dfde..155d9a7 100644
--- a/packages/nitrogen/generated/shared/c++/HybridHealthKitSpec.hpp
+++ b/packages/nitrogen/generated/shared/c++/HybridHealthKitSpec.hpp
@@ -25,11 +25,11 @@ namespace margelo::nitro::healthkit { struct WorkoutDataPoint; }
namespace margelo::nitro::healthkit { struct BackgroundSyncConfig; }
#include
-#include "QuantityDataPoint.hpp"
-#include
#include
-#include
+#include
#include
+#include "QuantityDataPoint.hpp"
+#include
#include "CategoryDataPoint.hpp"
#include "HealthData.hpp"
#include "WorkoutDataPoint.hpp"
@@ -68,7 +68,7 @@ namespace margelo::nitro::healthkit {
public:
// Methods
- virtual std::shared_ptr> requestAuthorization() = 0;
+ virtual std::shared_ptr> requestAuthorization(const std::optional>& readTypes) = 0;
virtual std::shared_ptr>> getQuantityData(const std::string& type, std::chrono::system_clock::time_point startDate, std::chrono::system_clock::time_point endDate, const std::optional& aggregationType, std::optional useCache, std::optional cacheTTL) = 0;
virtual std::shared_ptr> getAggregatedQuantity(const std::string& type, std::chrono::system_clock::time_point startDate, std::chrono::system_clock::time_point endDate, const std::string& aggregationType, std::optional useCache, std::optional cacheTTL) = 0;
virtual std::shared_ptr>> getCategoryData(const std::string& type, std::chrono::system_clock::time_point startDate, std::chrono::system_clock::time_point endDate, std::optional useCache, std::optional cacheTTL) = 0;
diff --git a/packages/src/index.ts b/packages/src/index.ts
index 71d966f..826af5c 100644
--- a/packages/src/index.ts
+++ b/packages/src/index.ts
@@ -48,6 +48,7 @@ export type {
export {
HealthKitQuantityType,
HealthKitCategoryType,
+ HealthKitWorkoutType,
TimeRange,
AggregationType
} from './specs/HealthKit.nitro'
diff --git a/packages/src/specs/HealthKit.nitro.ts b/packages/src/specs/HealthKit.nitro.ts
index c5a9d8f..94b6604 100644
--- a/packages/src/specs/HealthKit.nitro.ts
+++ b/packages/src/specs/HealthKit.nitro.ts
@@ -212,6 +212,10 @@ export enum HealthKitCategoryType {
TREATMENTS = 'HKCategoryTypeIdentifierTreatments'
}
+export enum HealthKitWorkoutType {
+ WORKOUT = 'HKWorkoutTypeIdentifier'
+}
+
export enum TimeRange {
TODAY = 'today',
YESTERDAY = 'yesterday',
@@ -314,9 +318,10 @@ export interface HealthChangeEvent {
export interface HealthKit extends HybridObject<{ ios: 'swift', android: 'kotlin' }> {
/**
- * Request HealthKit authorizations.
+ * Request read authorization for the provided health data types.
+ * Omitting readTypes preserves the default authorization set.
*/
- requestAuthorization(): Promise
+ requestAuthorization(readTypes?: string[]): Promise
/**
* Fetch raw quantity samples (individual data points).
@@ -404,6 +409,7 @@ export interface HealthKit extends HybridObject<{ ios: 'swift', android: 'kotlin
* Check the authorization status for a specific type.
* @param type Data type (quantity or category)
* @returns 'notDetermined' | 'sharingDenied' | 'sharingAuthorized'
+ * On iOS, HealthKit only reveals whether another read authorization request is needed.
*/
checkAuthorizationStatus(type: string): Promise
@@ -501,4 +507,4 @@ export interface HealthKit extends HybridObject<{ ios: 'swift', android: 'kotlin
* Report whether a background synchronization task is registered.
*/
isBackgroundSyncRegistered(): Promise
-}
\ No newline at end of file
+}