@interval-health/capacitor-health 1.0.2 → 1.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,324 +1,861 @@
1
- # @capgo/capacitor-health
2
- <a href="https://capgo.app/"><img src='https://raw.githubusercontent.com/Cap-go/capgo/main/assets/capgo_banner.png' alt='Capgo - Instant updates for capacitor'/></a>
1
+ # @interval-health/capacitor-health
3
2
 
4
- <div align="center">
5
- <h2><a href="https://capgo.app/?ref=plugin_health"> ➡️ Get Instant updates for your App with Capgo</a></h2>
6
- <h2><a href="https://capgo.app/consulting/?ref=plugin_health"> Missing a feature? We’ll build the plugin for you 💪</a></h2>
7
- </div>
3
+ A Capacitor plugin to interact with health data from **Apple HealthKit** (iOS) and **Health Connect** (Android). This plugin provides a unified JavaScript API to read and write health and fitness data across both platforms.
8
4
 
9
- Capacitor plugin to read and write health metrics via Apple HealthKit (iOS) and Health Connect (Android). The TypeScript API keeps the same data types and units across platforms so you can build once and deploy everywhere.
5
+ ---
10
6
 
11
- ## Why Capacitor Health?
7
+ ## 📦 Installation
12
8
 
13
- The only **free**, **unified** health data plugin for Capacitor supporting the latest native APIs:
9
+ ```bash
10
+ npm install @interval-health/capacitor-health
11
+ ```
12
+
13
+ After installation, sync your native projects:
14
+
15
+ ```bash
16
+ npx cap sync
17
+ ```
14
18
 
15
- - **Health Connect (Android)** - Uses Google's newest health platform (replaces deprecated Google Fit)
16
- - **HealthKit (iOS)** - Full integration with Apple's health framework
17
- - **Unified API** - Same TypeScript interface across platforms with consistent units
18
- - **Multiple metrics** - Steps, distance, calories, heart rate, weight
19
- - **Read & Write** - Query historical data and save new health entries
20
- - **Modern standards** - Supports Android 8.0+ and iOS 14+
21
- - **Modern package management** - Supports both Swift Package Manager (SPM) and CocoaPods (SPM-ready for Capacitor 8)
19
+ ---
22
20
 
23
- Perfect for fitness apps, health trackers, wellness platforms, and medical applications.
21
+ ## 🚀 Supported Platforms
24
22
 
25
- ## Documentation
23
+ | Platform | Implementation | Minimum Version |
24
+ |----------|---------------|-----------------|
25
+ | **iOS** | Apple HealthKit | iOS 13.0+ |
26
+ | **Android** | Health Connect | Android 9.0+ (API 28) |
27
+ | **Web** | Not supported | - |
26
28
 
27
- The most complete doc is available here: https://capgo.app/docs/plugins/health/
29
+ ### Platform Requirements
28
30
 
29
- ## Install
31
+ #### iOS
32
+ - Xcode 14.0 or later
33
+ - iOS deployment target: 13.0+
34
+ - You must add the required usage descriptions to your `Info.plist`
30
35
 
31
- ```bash
32
- npm install @capgo/capacitor-health
33
- npx cap sync
34
- ```
36
+ #### Android
37
+ - Android Studio Arctic Fox or later
38
+ - Minimum SDK: 28 (Android 9.0)
39
+ - Target SDK: 34+
40
+ - Health Connect app must be installed on the device
35
41
 
36
- ## iOS Setup
42
+ ---
37
43
 
38
- 1. Open your Capacitor application's Xcode workspace and enable the **HealthKit** capability.
39
- 2. Provide usage descriptions in `Info.plist` (update the copy for your product):
44
+ ## 🔐 Permissions & Setup
45
+
46
+ ### iOS Setup
47
+
48
+ Add the following keys to your `ios/App/App/Info.plist` file:
40
49
 
41
50
  ```xml
42
51
  <key>NSHealthShareUsageDescription</key>
43
- <string>This app reads your health data to personalise your experience.</string>
52
+ <string>This app needs access to read your health data.</string>
44
53
  <key>NSHealthUpdateUsageDescription</key>
45
- <string>This app writes new health entries that you explicitly create.</string>
54
+ <string>This app needs access to write health data.</string>
55
+ ```
56
+
57
+ Enable the **HealthKit** capability in your Xcode project:
58
+ 1. Open your project in Xcode
59
+ 2. Select your app target
60
+ 3. Go to **Signing & Capabilities**
61
+ 4. Click **+ Capability** and add **HealthKit**
62
+
63
+ ### Android Setup
64
+
65
+ Add the Health Connect permissions to your `android/app/src/main/AndroidManifest.xml`:
66
+
67
+ ```xml
68
+ <manifest>
69
+ <!-- Health Connect permissions -->
70
+ <uses-permission android:name="android.permission.health.READ_STEPS"/>
71
+ <uses-permission android:name="android.permission.health.WRITE_STEPS"/>
72
+ <uses-permission android:name="android.permission.health.READ_DISTANCE"/>
73
+ <uses-permission android:name="android.permission.health.WRITE_DISTANCE"/>
74
+ <uses-permission android:name="android.permission.health.READ_ACTIVE_CALORIES_BURNED"/>
75
+ <uses-permission android:name="android.permission.health.WRITE_ACTIVE_CALORIES_BURNED"/>
76
+ <uses-permission android:name="android.permission.health.READ_HEART_RATE"/>
77
+ <uses-permission android:name="android.permission.health.WRITE_HEART_RATE"/>
78
+ <uses-permission android:name="android.permission.health.READ_WEIGHT"/>
79
+ <uses-permission android:name="android.permission.health.WRITE_WEIGHT"/>
80
+ <uses-permission android:name="android.permission.health.READ_SLEEP"/>
81
+ <uses-permission android:name="android.permission.health.WRITE_SLEEP"/>
82
+
83
+ <application>
84
+ <!-- Required for Health Connect integration -->
85
+ <activity-alias
86
+ android:name="ViewPermissionUsageActivity"
87
+ android:exported="true"
88
+ android:targetActivity=".MainActivity"
89
+ android:permission="android.permission.START_VIEW_PERMISSION_USAGE">
90
+ <intent-filter>
91
+ <action android:name="androidx.health.ACTION_SHOW_PERMISSIONS_RATIONALE" />
92
+ </intent-filter>
93
+ </activity-alias>
94
+ </application>
95
+ </manifest>
96
+ ```
97
+
98
+ **Note**: Users must have the **Health Connect** app installed on their Android device. If not installed, the plugin will report that health data is unavailable.
99
+
100
+ ---
101
+
102
+ ## 📖 Usage Guide
103
+
104
+ ### Import the Plugin
105
+
106
+ ```typescript
107
+ import { Health } from '@interval-health/capacitor-health';
108
+ ```
109
+
110
+ ### Complete Workflow Example
111
+
112
+ Here's a typical flow for working with health data:
113
+
114
+ ```typescript
115
+ import { Health } from '@interval-health/capacitor-health';
116
+
117
+ async function setupHealthData() {
118
+ // 1. Check if health services are available
119
+ const availability = await Health.isAvailable();
120
+
121
+ if (!availability.available) {
122
+ console.error('Health data unavailable:', availability.reason);
123
+ return;
124
+ }
125
+
126
+ // 2. Request authorization for data types
127
+ const authStatus = await Health.requestAuthorization({
128
+ read: ['steps', 'heartRate', 'weight', 'sleep'],
129
+ write: ['steps', 'weight']
130
+ });
131
+
132
+ console.log('Authorized to read:', authStatus.readAuthorized);
133
+ console.log('Denied to read:', authStatus.readDenied);
134
+
135
+ // 3. Read health samples
136
+ const stepsData = await Health.readSamples({
137
+ dataType: 'steps',
138
+ startDate: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000).toISOString(), // 7 days ago
139
+ endDate: new Date().toISOString(),
140
+ limit: 100,
141
+ ascending: false
142
+ });
143
+
144
+ console.log('Steps data:', stepsData.samples);
145
+
146
+ // 4. Write a health sample
147
+ await Health.saveSample({
148
+ dataType: 'weight',
149
+ value: 70.5,
150
+ startDate: new Date().toISOString()
151
+ });
152
+
153
+ console.log('Weight saved successfully!');
154
+ }
155
+ ```
156
+
157
+ ---
158
+
159
+ ## 📚 API Reference
160
+
161
+ ### Health Data Types
162
+
163
+ The plugin supports the following health data types:
164
+
165
+ ```typescript
166
+ export type HealthDataType =
167
+ | 'steps'
168
+ | 'distance'
169
+ | 'calories'
170
+ | 'heartRate'
171
+ | 'weight'
172
+ | 'sleep'
173
+ | 'mobility'
174
+ | 'activity'
175
+ | 'heart'
176
+ | 'body';
46
177
  ```
47
178
 
48
- ## Android Setup
179
+ | Data Type | Description | Unit | iOS | Android | Read | Write |
180
+ |-----------|-------------|------|-----|---------|------|-------|
181
+ | `steps` | Step count | count | ✅ | ✅ | ✅ | ✅ |
182
+ | `distance` | Walking/running distance | meter | ✅ | ✅ | ✅ | ✅ |
183
+ | `calories` | Active calories burned | kilocalorie | ✅ | ✅ | ✅ | ✅ |
184
+ | `heartRate` | Heart rate | bpm (beats per minute) | ✅ | ✅ | ✅ | ✅ |
185
+ | `weight` | Body weight | kilogram | ✅ | ✅ | ✅ | ✅ |
186
+ | `sleep` | Sleep duration and stages | minute | ✅ | ❌ | ✅ | ✅ |
187
+ | `mobility` | Mobility metrics | mixed | ✅ | ❌ | ✅ | ❌ |
188
+ | `activity` | Activity metrics | mixed | ✅ | ❌ | ✅ | ❌ |
189
+ | `heart` | Heart health metrics | mixed | ✅ | ❌ | ✅ | ❌ |
190
+ | `body` | Body measurements | mixed | ✅ | ❌ | ✅ | ❌ |
191
+
192
+ **Platform Support Notes**:
193
+ - **iOS** supports all 10 data types
194
+ - **Android** supports only 5 basic data types: `steps`, `distance`, `calories`, `heartRate`, and `weight`
195
+ - The `sleep`, `mobility`, `activity`, `heart`, and `body` types are **iOS-only** and not available on Android
196
+ - Composite types (`mobility`, `activity`, `heart`, `body`) are **read-only** on iOS
197
+
198
+ ### Sleep States
199
+
200
+ When reading sleep data, each sample may include a `sleepState` property:
201
+
202
+ | State | Description |
203
+ |-------|-------------|
204
+ | `inBed` | User is in bed but not necessarily asleep |
205
+ | `asleep` | General sleep state (when specific stage unknown) |
206
+ | `awake` | User is awake during sleep period |
207
+ | `asleepCore` | Core/light sleep stage |
208
+ | `asleepDeep` | Deep sleep stage |
209
+ | `asleepREM` | REM (Rapid Eye Movement) sleep stage |
210
+ | `unknown` | Sleep state could not be determined |
211
+
212
+ ---
213
+
214
+ ## 🔧 Methods
215
+
216
+ ### isAvailable()
49
217
 
50
- This plugin now uses [Health Connect](https://developer.android.com/health-and-fitness/guides/health-connect) instead of Google Fit. Make sure your app meets the requirements below:
218
+ Check if health services are available on the current platform.
51
219
 
52
- 1. **Min SDK 26+.** Health Connect is only available on Android 8.0 (API 26) and above. The plugin’s Gradle setup already targets this level.
53
- 2. **Declare Health permissions.** The plugin manifest ships with the required `<uses-permission>` declarations (`READ_/WRITE_STEPS`, `READ_/WRITE_DISTANCE`, `READ_/WRITE_ACTIVE_CALORIES_BURNED`, `READ_/WRITE_HEART_RATE`, `READ_/WRITE_WEIGHT`). Your app does not need to duplicate them, but you must surface a user-facing rationale because the permissions are considered health sensitive.
54
- 3. **Ensure Health Connect is installed.** Devices on Android 14+ include it by default. For earlier versions the user must install *Health Connect by Android* from the Play Store. The `Health.isAvailable()` helper exposes the current status so you can prompt accordingly.
55
- 4. **Request runtime access.** The plugin opens the Health Connect permission UI when you call `requestAuthorization`. You should still handle denial flows (e.g., show a message if `checkAuthorization` reports missing scopes).
220
+ ```typescript
221
+ Health.isAvailable(): Promise<AvailabilityResult>
222
+ ```
56
223
 
57
- If you already used Google Fit in your project you can remove the associated dependencies (`play-services-fitness`, `play-services-auth`, OAuth configuration, etc.).
224
+ **Returns**: `Promise<AvailabilityResult>`
58
225
 
59
- ## Usage
226
+ **Example**:
227
+ ```typescript
228
+ const result = await Health.isAvailable();
60
229
 
61
- ```ts
62
- import { Health } from '@capgo/capacitor-health';
230
+ if (result.available) {
231
+ console.log('Health services available on:', result.platform);
232
+ } else {
233
+ console.log('Unavailable reason:', result.reason);
234
+ }
235
+ ```
63
236
 
64
- // Verify that the native health SDK is present on this device
65
- const availability = await Health.isAvailable();
66
- if (!availability.available) {
67
- console.warn('Health access unavailable:', availability.reason);
237
+ **Response Example**:
238
+ ```typescript
239
+ {
240
+ available: true,
241
+ platform: 'ios' // or 'android'
68
242
  }
69
243
 
70
- // Ask for separate read/write access scopes
71
- await Health.requestAuthorization({
72
- read: ['steps', 'heartRate', 'weight', 'sleep'],
73
- write: ['weight', 'sleep'],
244
+ // When unavailable:
245
+ {
246
+ available: false,
247
+ platform: 'android',
248
+ reason: 'Health Connect is unavailable on this device.'
249
+ }
250
+ ```
251
+
252
+ ---
253
+
254
+ ### requestAuthorization()
255
+
256
+ Request permission to read and/or write specific health data types. This will show the platform's native permission dialog.
257
+
258
+ ```typescript
259
+ Health.requestAuthorization(options: AuthorizationOptions): Promise<AuthorizationStatus>
260
+ ```
261
+
262
+ **Parameters**:
263
+ - `options.read` (optional): Array of data types you want to read
264
+ - `options.write` (optional): Array of data types you want to write
265
+
266
+ **Returns**: `Promise<AuthorizationStatus>`
267
+
268
+ **Example**:
269
+ ```typescript
270
+ const status = await Health.requestAuthorization({
271
+ read: ['steps', 'heartRate', 'sleep'],
272
+ write: ['steps', 'weight']
273
+ });
274
+
275
+ console.log('Read authorized:', status.readAuthorized);
276
+ console.log('Read denied:', status.readDenied);
277
+ console.log('Write authorized:', status.writeAuthorized);
278
+ console.log('Write denied:', status.writeDenied);
279
+ ```
280
+
281
+ **Response Example**:
282
+ ```typescript
283
+ {
284
+ readAuthorized: ['steps', 'heartRate'],
285
+ readDenied: ['sleep'],
286
+ writeAuthorized: ['steps', 'weight'],
287
+ writeDenied: []
288
+ }
289
+ ```
290
+
291
+ **Important Notes**:
292
+ - On **iOS**, the HealthKit API doesn't reveal whether the user granted or denied read permission (for privacy reasons). The `readAuthorized` array will contain all requested types, even if some were denied.
293
+ - On **Android**, Health Connect provides accurate permission status.
294
+ - You must call this method before reading or writing health data.
295
+
296
+ ---
297
+
298
+ ### checkAuthorization()
299
+
300
+ Check the current authorization status without prompting the user.
301
+
302
+ ```typescript
303
+ Health.checkAuthorization(options: AuthorizationOptions): Promise<AuthorizationStatus>
304
+ ```
305
+
306
+ **Parameters**: Same as `requestAuthorization()`
307
+
308
+ **Returns**: `Promise<AuthorizationStatus>`
309
+
310
+ **Example**:
311
+ ```typescript
312
+ const status = await Health.checkAuthorization({
313
+ read: ['steps', 'heartRate'],
314
+ write: ['weight']
74
315
  });
75
316
 
76
- // Query the last 50 step samples from the past 24 hours
77
- const { samples } = await Health.readSamples({
317
+ if (status.readAuthorized.includes('steps')) {
318
+ // We have permission to read steps
319
+ await readStepsData();
320
+ }
321
+ ```
322
+
323
+ **Response Example**: Same structure as `requestAuthorization()`
324
+
325
+ ---
326
+
327
+ ### readSamples()
328
+
329
+ Read health samples for a specific data type within a time range.
330
+
331
+ ```typescript
332
+ Health.readSamples(options: QueryOptions): Promise<ReadSamplesResult>
333
+ ```
334
+
335
+ **Parameters**:
336
+ - `options.dataType` (required): The type of health data to retrieve
337
+ - `options.startDate` (optional): ISO 8601 start date (inclusive). Defaults to 24 hours ago
338
+ - `options.endDate` (optional): ISO 8601 end date (exclusive). Defaults to now
339
+ - `options.limit` (optional): Maximum number of samples to return. Defaults to 100
340
+ - `options.ascending` (optional): Sort results by start date ascending. Defaults to false (descending)
341
+
342
+ **Returns**: `Promise<ReadSamplesResult>`
343
+
344
+ **Example**:
345
+ ```typescript
346
+ // Read last 7 days of step data
347
+ const result = await Health.readSamples({
78
348
  dataType: 'steps',
79
- startDate: new Date(Date.now() - 24 * 60 * 60 * 1000).toISOString(),
349
+ startDate: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000).toISOString(),
80
350
  endDate: new Date().toISOString(),
81
351
  limit: 50,
352
+ ascending: false
82
353
  });
83
354
 
84
- // Persist a new body-weight entry (kilograms by default)
85
- await Health.saveSample({
86
- dataType: 'weight',
87
- value: 74.3,
355
+ result.samples.forEach(sample => {
356
+ console.log(`${sample.value} ${sample.unit} on ${sample.startDate}`);
88
357
  });
358
+ ```
89
359
 
90
- // Query sleep data from the past week
91
- const { samples: sleepSamples } = await Health.readSamples({
360
+ **Response Example**:
361
+ ```typescript
362
+ {
363
+ samples: [
364
+ {
365
+ dataType: 'steps',
366
+ value: 8543,
367
+ unit: 'count',
368
+ startDate: '2025-12-17T00:00:00.000Z',
369
+ endDate: '2025-12-17T23:59:59.999Z',
370
+ sourceName: 'Apple Watch',
371
+ sourceId: 'com.apple.health'
372
+ },
373
+ {
374
+ dataType: 'steps',
375
+ value: 12032,
376
+ unit: 'count',
377
+ startDate: '2025-12-16T00:00:00.000Z',
378
+ endDate: '2025-12-16T23:59:59.999Z',
379
+ sourceName: 'iPhone',
380
+ sourceId: 'com.apple.health'
381
+ }
382
+ ]
383
+ }
384
+ ```
385
+
386
+ **Sleep Data Example**:
387
+ ```typescript
388
+ const sleepResult = await Health.readSamples({
92
389
  dataType: 'sleep',
93
- startDate: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000).toISOString(),
94
- endDate: new Date().toISOString(),
390
+ startDate: '2025-12-16T00:00:00.000Z',
391
+ endDate: '2025-12-17T00:00:00.000Z'
95
392
  });
96
- // Each sleep sample includes: value (duration in minutes), sleepState (stage), startDate, endDate
97
- ```
98
393
 
99
- ### Supported data types
394
+ // Sleep samples include sleepState information
395
+ sleepResult.samples.forEach(sample => {
396
+ console.log(`Sleep: ${sample.value} ${sample.unit}, State: ${sample.sleepState}`);
397
+ });
398
+ ```
100
399
 
101
- | Identifier | Default unit | Notes |
102
- | ---------- | ------------- | ----- |
103
- | `steps` | `count` | Step count deltas |
104
- | `distance` | `meter` | Walking / running distance |
105
- | `calories` | `kilocalorie` | Active energy burned |
106
- | `heartRate`| `bpm` | Beats per minute |
107
- | `weight` | `kilogram` | Body mass |
108
- | `sleep` | `minute` | Sleep sessions with stages (inBed, asleep, awake, asleepCore, asleepDeep, asleepREM) |
400
+ **Response Example for Sleep**:
401
+ ```typescript
402
+ {
403
+ samples: [
404
+ {
405
+ dataType: 'sleep',
406
+ value: 450,
407
+ unit: 'minute',
408
+ startDate: '2025-12-16T22:30:00.000Z',
409
+ endDate: '2025-12-17T06:00:00.000Z',
410
+ sleepState: 'asleepDeep',
411
+ sourceName: 'Sleep App'
412
+ }
413
+ ]
414
+ }
415
+ ```
109
416
 
110
- All write operations expect the default unit shown above. On Android the `metadata` option is currently ignored by Health Connect.
417
+ ---
111
418
 
112
- ## API
419
+ ### saveSample()
113
420
 
114
- <docgen-index>
421
+ Write a single health sample to the native health store.
115
422
 
116
- * [`isAvailable()`](#isavailable)
117
- * [`requestAuthorization(...)`](#requestauthorization)
118
- * [`checkAuthorization(...)`](#checkauthorization)
119
- * [`readSamples(...)`](#readsamples)
120
- * [`saveSample(...)`](#savesample)
121
- * [`getPluginVersion()`](#getpluginversion)
122
- * [Interfaces](#interfaces)
123
- * [Type Aliases](#type-aliases)
423
+ ```typescript
424
+ Health.saveSample(options: WriteSampleOptions): Promise<void>
425
+ ```
124
426
 
125
- </docgen-index>
427
+ **Parameters**:
428
+ - `options.dataType` (required): The type of health data to save
429
+ - `options.value` (required): The numeric value
430
+ - `options.unit` (optional): Unit override (must match the data type's expected unit)
431
+ - `options.startDate` (optional): ISO 8601 start date. Defaults to now
432
+ - `options.endDate` (optional): ISO 8601 end date. Defaults to startDate
433
+ - `options.metadata` (optional): Additional key-value metadata (platform support varies)
126
434
 
127
- <docgen-api>
128
- <!--Update the source file JSDoc comments and rerun docgen to update the docs below-->
435
+ **Returns**: `Promise<void>`
129
436
 
130
- ### isAvailable()
437
+ **Example - Save Weight**:
438
+ ```typescript
439
+ await Health.saveSample({
440
+ dataType: 'weight',
441
+ value: 72.5,
442
+ startDate: new Date().toISOString()
443
+ });
444
+ ```
131
445
 
446
+ **Example - Save Steps with Time Range**:
132
447
  ```typescript
133
- isAvailable() => Promise<AvailabilityResult>
448
+ const workoutStart = new Date('2025-12-17T10:00:00.000Z');
449
+ const workoutEnd = new Date('2025-12-17T11:30:00.000Z');
450
+
451
+ await Health.saveSample({
452
+ dataType: 'steps',
453
+ value: 5000,
454
+ startDate: workoutStart.toISOString(),
455
+ endDate: workoutEnd.toISOString(),
456
+ metadata: {
457
+ 'workout': 'morning run',
458
+ 'location': 'park'
459
+ }
460
+ });
134
461
  ```
135
462
 
136
- Returns whether the current platform supports the native health SDK.
463
+ **Example - Save Heart Rate**:
464
+ ```typescript
465
+ await Health.saveSample({
466
+ dataType: 'heartRate',
467
+ value: 75,
468
+ unit: 'bpm',
469
+ startDate: new Date().toISOString()
470
+ });
471
+ ```
137
472
 
138
- **Returns:** <code>Promise&lt;<a href="#availabilityresult">AvailabilityResult</a>&gt;</code>
473
+ **Unit Validation**: The plugin validates that the provided unit matches the expected unit for the data type. For example:
474
+ - `steps` expects `count`
475
+ - `distance` expects `meter`
476
+ - `calories` expects `kilocalorie`
477
+ - `heartRate` expects `bpm`
478
+ - `weight` expects `kilogram`
139
479
 
140
- --------------------
480
+ ---
141
481
 
482
+ ### getPluginVersion()
142
483
 
143
- ### requestAuthorization(...)
484
+ Get the current version of the native plugin.
144
485
 
145
486
  ```typescript
146
- requestAuthorization(options: AuthorizationOptions) => Promise<AuthorizationStatus>
487
+ Health.getPluginVersion(): Promise<{ version: string }>
147
488
  ```
148
489
 
149
- Requests read/write access to the provided data types.
150
-
151
- | Param | Type |
152
- | ------------- | --------------------------------------------------------------------- |
153
- | **`options`** | <code><a href="#authorizationoptions">AuthorizationOptions</a></code> |
490
+ **Returns**: `Promise<{ version: string }>`
154
491
 
155
- **Returns:** <code>Promise&lt;<a href="#authorizationstatus">AuthorizationStatus</a>&gt;</code>
492
+ **Example**:
493
+ ```typescript
494
+ const { version } = await Health.getPluginVersion();
495
+ console.log('Plugin version:', version);
496
+ ```
156
497
 
157
- --------------------
498
+ ---
158
499
 
500
+ ## 💡 Common Usage Patterns
159
501
 
160
- ### checkAuthorization(...)
502
+ ### 1. Displaying Daily Step Count
161
503
 
162
504
  ```typescript
163
- checkAuthorization(options: AuthorizationOptions) => Promise<AuthorizationStatus>
505
+ async function getDailySteps() {
506
+ const today = new Date();
507
+ today.setHours(0, 0, 0, 0);
508
+
509
+ const result = await Health.readSamples({
510
+ dataType: 'steps',
511
+ startDate: today.toISOString(),
512
+ endDate: new Date().toISOString()
513
+ });
514
+
515
+ const totalSteps = result.samples.reduce((sum, sample) => sum + sample.value, 0);
516
+ console.log('Steps today:', totalSteps);
517
+
518
+ return totalSteps;
519
+ }
164
520
  ```
165
521
 
166
- Checks authorization status for the provided data types without prompting the user.
167
-
168
- | Param | Type |
169
- | ------------- | --------------------------------------------------------------------- |
170
- | **`options`** | <code><a href="#authorizationoptions">AuthorizationOptions</a></code> |
522
+ ### 2. Weekly Activity Summary
171
523
 
172
- **Returns:** <code>Promise&lt;<a href="#authorizationstatus">AuthorizationStatus</a>&gt;</code>
524
+ ```typescript
525
+ async function getWeeklySummary() {
526
+ const weekAgo = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000);
527
+
528
+ const [steps, distance, calories] = await Promise.all([
529
+ Health.readSamples({
530
+ dataType: 'steps',
531
+ startDate: weekAgo.toISOString(),
532
+ limit: 500
533
+ }),
534
+ Health.readSamples({
535
+ dataType: 'distance',
536
+ startDate: weekAgo.toISOString(),
537
+ limit: 500
538
+ }),
539
+ Health.readSamples({
540
+ dataType: 'calories',
541
+ startDate: weekAgo.toISOString(),
542
+ limit: 500
543
+ })
544
+ ]);
545
+
546
+ return {
547
+ totalSteps: steps.samples.reduce((sum, s) => sum + s.value, 0),
548
+ totalDistance: distance.samples.reduce((sum, s) => sum + s.value, 0),
549
+ totalCalories: calories.samples.reduce((sum, s) => sum + s.value, 0)
550
+ };
551
+ }
552
+ ```
173
553
 
174
- --------------------
554
+ ### 3. Logging Weight Over Time
175
555
 
556
+ ```typescript
557
+ async function getWeightHistory(days: number = 30) {
558
+ const startDate = new Date(Date.now() - days * 24 * 60 * 60 * 1000);
559
+
560
+ const result = await Health.readSamples({
561
+ dataType: 'weight',
562
+ startDate: startDate.toISOString(),
563
+ ascending: true
564
+ });
565
+
566
+ return result.samples.map(sample => ({
567
+ date: new Date(sample.startDate).toLocaleDateString(),
568
+ weight: sample.value,
569
+ unit: sample.unit
570
+ }));
571
+ }
572
+ ```
176
573
 
177
- ### readSamples(...)
574
+ ### 4. Sleep Analysis
178
575
 
179
576
  ```typescript
180
- readSamples(options: QueryOptions) => Promise<ReadSamplesResult>
577
+ async function getLastNightSleep() {
578
+ const yesterday = new Date();
579
+ yesterday.setDate(yesterday.getDate() - 1);
580
+ yesterday.setHours(18, 0, 0, 0); // Start from 6 PM yesterday
581
+
582
+ const result = await Health.readSamples({
583
+ dataType: 'sleep',
584
+ startDate: yesterday.toISOString(),
585
+ endDate: new Date().toISOString()
586
+ });
587
+
588
+ // Calculate total sleep time and breakdown by stage
589
+ const sleepByStage: Record<string, number> = {};
590
+ let totalSleep = 0;
591
+
592
+ result.samples.forEach(sample => {
593
+ const state = sample.sleepState || 'unknown';
594
+ sleepByStage[state] = (sleepByStage[state] || 0) + sample.value;
595
+ totalSleep += sample.value;
596
+ });
597
+
598
+ return {
599
+ totalMinutes: totalSleep,
600
+ totalHours: (totalSleep / 60).toFixed(1),
601
+ breakdown: sleepByStage
602
+ };
603
+ }
181
604
  ```
182
605
 
183
- Reads samples for the given data type within the specified time frame.
606
+ ### 5. Recording a Workout
184
607
 
185
- | Param | Type |
186
- | ------------- | ----------------------------------------------------- |
187
- | **`options`** | <code><a href="#queryoptions">QueryOptions</a></code> |
188
-
189
- **Returns:** <code>Promise&lt;<a href="#readsamplesresult">ReadSamplesResult</a>&gt;</code>
608
+ ```typescript
609
+ async function recordWorkout() {
610
+ const workoutStart = new Date(Date.now() - 45 * 60 * 1000); // 45 minutes ago
611
+ const workoutEnd = new Date();
612
+
613
+ // Save multiple metrics from the workout
614
+ await Promise.all([
615
+ Health.saveSample({
616
+ dataType: 'steps',
617
+ value: 4500,
618
+ startDate: workoutStart.toISOString(),
619
+ endDate: workoutEnd.toISOString()
620
+ }),
621
+ Health.saveSample({
622
+ dataType: 'distance',
623
+ value: 3500, // 3.5 km in meters
624
+ startDate: workoutStart.toISOString(),
625
+ endDate: workoutEnd.toISOString()
626
+ }),
627
+ Health.saveSample({
628
+ dataType: 'calories',
629
+ value: 320,
630
+ startDate: workoutStart.toISOString(),
631
+ endDate: workoutEnd.toISOString()
632
+ })
633
+ ]);
634
+
635
+ console.log('Workout recorded successfully!');
636
+ }
637
+ ```
190
638
 
191
- --------------------
639
+ ---
192
640
 
641
+ ## ⚠️ Error Handling
193
642
 
194
- ### saveSample(...)
643
+ ### Common Errors and Solutions
195
644
 
196
645
  ```typescript
197
- saveSample(options: WriteSampleOptions) => Promise<void>
646
+ async function safeHealthRead() {
647
+ try {
648
+ // Check availability first
649
+ const availability = await Health.isAvailable();
650
+ if (!availability.available) {
651
+ throw new Error(`Health unavailable: ${availability.reason}`);
652
+ }
653
+
654
+ // Request authorization
655
+ const authStatus = await Health.requestAuthorization({
656
+ read: ['steps']
657
+ });
658
+
659
+ if (authStatus.readDenied.includes('steps')) {
660
+ throw new Error('User denied permission to read steps');
661
+ }
662
+
663
+ // Read data
664
+ const result = await Health.readSamples({
665
+ dataType: 'steps',
666
+ startDate: new Date(Date.now() - 24 * 60 * 60 * 1000).toISOString()
667
+ });
668
+
669
+ return result.samples;
670
+
671
+ } catch (error) {
672
+ console.error('Health data error:', error);
673
+
674
+ // Handle specific error cases
675
+ if (error.message.includes('unavailable')) {
676
+ alert('Please install Health Connect (Android) or enable HealthKit (iOS)');
677
+ } else if (error.message.includes('permission')) {
678
+ alert('Please grant health data permissions in settings');
679
+ } else if (error.message.includes('Unsupported data type')) {
680
+ alert('This health metric is not supported on your device');
681
+ } else {
682
+ alert('Unable to access health data. Please try again.');
683
+ }
684
+
685
+ return [];
686
+ }
687
+ }
198
688
  ```
199
689
 
200
- Writes a single sample to the native health store.
690
+ ### Error Types
201
691
 
202
- | Param | Type |
203
- | ------------- | ----------------------------------------------------------------- |
204
- | **`options`** | <code><a href="#writesampleoptions">WriteSampleOptions</a></code> |
692
+ | Error | Cause | Solution |
693
+ |-------|-------|----------|
694
+ | `Health data is not available` | HealthKit/Health Connect not available | Check device compatibility |
695
+ | `Unsupported data type` | Invalid dataType parameter | Use one of the supported data types |
696
+ | `dataType is required` | Missing required parameter | Provide dataType in options |
697
+ | `value is required` | Missing value for saveSample | Provide numeric value |
698
+ | `Invalid ISO 8601 date` | Malformed date string | Use proper ISO 8601 format: `new Date().toISOString()` |
699
+ | `endDate must be greater than startDate` | Invalid date range | Ensure endDate >= startDate |
700
+ | `Health Connect needs an update` | Outdated Health Connect app | Update Health Connect from Play Store |
701
+ | `Unsupported unit` | Wrong unit for data type | Use the correct unit or omit to use default |
205
702
 
206
- --------------------
703
+ ---
207
704
 
705
+ ## 🔒 Privacy & Security
208
706
 
209
- ### getPluginVersion()
707
+ ### iOS Privacy Considerations
210
708
 
211
- ```typescript
212
- getPluginVersion() => Promise<{ version: string; }>
213
- ```
709
+ - **HealthKit data never leaves the device** unless explicitly shared by your app
710
+ - Apple's HealthKit restricts read authorization status for privacy—your app cannot definitively know if read permission was denied
711
+ - Always provide clear explanations in your usage description strings
712
+ - Consider implementing fallback flows if users deny permissions
713
+
714
+ ### Android Privacy Considerations
214
715
 
215
- Get the native Capacitor plugin version
716
+ - Health Connect provides transparent permission management
717
+ - Users can revoke permissions at any time through system settings
718
+ - Your app should handle permission changes gracefully
719
+ - Health Connect shows users which apps access their data
216
720
 
217
- **Returns:** <code>Promise&lt;{ version: string; }&gt;</code>
721
+ ### Best Practices
218
722
 
219
- --------------------
723
+ 1. **Request only what you need**: Don't request access to all data types if you only need steps
724
+ 2. **Explain before asking**: Show UI explaining why you need health data before calling `requestAuthorization()`
725
+ 3. **Handle denials gracefully**: Provide alternative functionality if permissions are denied
726
+ 4. **Respect user privacy**: Don't store sensitive health data on external servers without explicit consent
727
+ 5. **Test permission flows**: Test your app's behavior when permissions are denied or revoked
220
728
 
729
+ ---
221
730
 
222
- ### Interfaces
731
+ ## 🐛 Known Limitations & Issues
223
732
 
733
+ ### iOS Limitations
224
734
 
225
- #### AvailabilityResult
735
+ 1. **Read Authorization Status**: HealthKit doesn't reveal whether users denied read permissions (privacy feature)
736
+ 2. **Background Access**: Reading health data in the background requires additional setup with Background Modes capability
737
+ 3. **Composite Types**: `mobility`, `activity`, `heart`, and `body` are iOS-only aggregate types that return data from multiple HealthKit sources
738
+ 4. **Write Authorization**: Apps can only write data types they created or have explicit write permission for
226
739
 
227
- | Prop | Type | Description |
228
- | --------------- | ---------------------------------------- | ------------------------------------------------------ |
229
- | **`available`** | <code>boolean</code> | |
230
- | **`platform`** | <code>'ios' \| 'android' \| 'web'</code> | Platform specific details (for debugging/diagnostics). |
231
- | **`reason`** | <code>string</code> | |
740
+ ### Android Limitations
232
741
 
742
+ 1. **Health Connect Required**: Users must have the Health Connect app installed (available on Android 9+)
743
+ 2. **Device Support**: Not all Android devices support Health Connect (mainly newer devices)
744
+ 3. **Limited Data Types**: Android implementation supports fewer composite types than iOS
745
+ 4. **API Level**: Requires minimum API level 28 (Android 9.0)
233
746
 
234
- #### AuthorizationStatus
747
+ ### General Limitations
235
748
 
236
- | Prop | Type |
237
- | --------------------- | ----------------------------- |
238
- | **`readAuthorized`** | <code>HealthDataType[]</code> |
239
- | **`readDenied`** | <code>HealthDataType[]</code> |
240
- | **`writeAuthorized`** | <code>HealthDataType[]</code> |
241
- | **`writeDenied`** | <code>HealthDataType[]</code> |
749
+ 1. **No Web Support**: This plugin does not work on web platforms (browser)
750
+ 2. **Data Sync Delays**: Health data may take time to sync between devices/apps
751
+ 3. **Source Variability**: Different apps and devices may report the same metrics differently
752
+ 4. **Historical Data**: Very old data (>1 year) may not be available depending on device settings
753
+ 5. **Unit Conversions**: The plugin uses specific units for each data type—unit conversion must be done in your app code
242
754
 
755
+ ---
243
756
 
244
- #### AuthorizationOptions
757
+ ## 📱 Platform-Specific Notes
245
758
 
246
- | Prop | Type | Description |
247
- | ----------- | ----------------------------- | ------------------------------------------------------- |
248
- | **`read`** | <code>HealthDataType[]</code> | Data types that should be readable after authorization. |
249
- | **`write`** | <code>HealthDataType[]</code> | Data types that should be writable after authorization. |
759
+ ### iOS (HealthKit)
250
760
 
761
+ - Requires physical iOS device for testing (Simulator has limited support)
762
+ - Some health metrics require specific hardware (Apple Watch for certain heart rate measurements)
763
+ - Sleep data quality depends on the user's sleep tracking app (Apple Watch, third-party apps)
764
+ - HealthKit automatically aggregates data from multiple sources
251
765
 
252
- #### ReadSamplesResult
766
+ ### Android (Health Connect)
253
767
 
254
- | Prop | Type |
255
- | ------------- | --------------------------- |
256
- | **`samples`** | <code>HealthSample[]</code> |
768
+ - Health Connect must be installed separately on devices with Android 13 or lower
769
+ - Android 14+ includes Health Connect as a system service
770
+ - Health Connect serves as a centralized hub for health data from multiple apps
771
+ - Not all Android OEMs enable Health Connect on their devices
772
+ - Users control which apps can access Health Connect through system settings
257
773
 
774
+ ---
258
775
 
259
- #### HealthSample
776
+ ## 🧪 Testing
260
777
 
261
- | Prop | Type | Description |
262
- | ---------------- | --------------------------------------------------------- | --------------------------------------------------- |
263
- | **`dataType`** | <code><a href="#healthdatatype">HealthDataType</a></code> | |
264
- | **`value`** | <code>number</code> | |
265
- | **`unit`** | <code><a href="#healthunit">HealthUnit</a></code> | |
266
- | **`startDate`** | <code>string</code> | |
267
- | **`endDate`** | <code>string</code> | |
268
- | **`sourceName`** | <code>string</code> | |
269
- | **`sourceId`** | <code>string</code> | |
270
- | **`sleepState`** | <code><a href="#sleepstate">SleepState</a></code> | Sleep state (only present when dataType is 'sleep') |
778
+ ### Testing on iOS
271
779
 
780
+ 1. Use a physical device (Simulator has limited HealthKit support)
781
+ 2. Generate sample health data using the Health app or third-party apps
782
+ 3. Test with Apple Watch if testing watch-specific metrics
272
783
 
273
- #### QueryOptions
784
+ ### Testing on Android
274
785
 
275
- | Prop | Type | Description |
276
- | --------------- | --------------------------------------------------------- | ------------------------------------------------------------------ |
277
- | **`dataType`** | <code><a href="#healthdatatype">HealthDataType</a></code> | The type of data to retrieve from the health store. |
278
- | **`startDate`** | <code>string</code> | Inclusive ISO 8601 start date (defaults to now - 1 day). |
279
- | **`endDate`** | <code>string</code> | Exclusive ISO 8601 end date (defaults to now). |
280
- | **`limit`** | <code>number</code> | Maximum number of samples to return (defaults to 100). |
281
- | **`ascending`** | <code>boolean</code> | Return results sorted ascending by start date (defaults to false). |
786
+ 1. Install Health Connect from the Play Store (if not pre-installed)
787
+ 2. Use Health Connect's test data generator or third-party health apps
788
+ 3. Test permission flows thoroughly—users can grant/deny per-data-type
789
+ 4. Test on multiple Android versions (9, 10, 13, 14) for compatibility
282
790
 
791
+ ---
283
792
 
284
- #### WriteSampleOptions
793
+ ## 🤝 Contributing
285
794
 
286
- | Prop | Type | Description |
287
- | --------------- | --------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
288
- | **`dataType`** | <code><a href="#healthdatatype">HealthDataType</a></code> | |
289
- | **`value`** | <code>number</code> | |
290
- | **`unit`** | <code><a href="#healthunit">HealthUnit</a></code> | Optional unit override. If omitted, the default unit for the data type is used (count for `steps`, meter for `distance`, kilocalorie for `calories`, bpm for `heartRate`, kilogram for `weight`, minute for `sleep`). |
291
- | **`startDate`** | <code>string</code> | ISO 8601 start date for the sample. Defaults to now. |
292
- | **`endDate`** | <code>string</code> | ISO 8601 end date for the sample. Defaults to startDate. |
293
- | **`metadata`** | <code><a href="#record">Record</a>&lt;string, string&gt;</code> | Metadata key-value pairs forwarded to the native APIs where supported. |
795
+ Contributions are welcome! Please follow these guidelines:
294
796
 
797
+ 1. Fork the repository
798
+ 2. Create a feature branch
799
+ 3. Make your changes with clear commit messages
800
+ 4. Test on both iOS and Android
801
+ 5. Submit a pull request
295
802
 
296
- ### Type Aliases
803
+ Please ensure your code follows the existing code style and includes appropriate error handling.
297
804
 
805
+ ---
298
806
 
299
- #### HealthDataType
807
+ ## 📄 License
300
808
 
301
- <code>'steps' | 'distance' | 'calories' | 'heartRate' | 'weight' | 'sleep'</code>
809
+ This project is licensed under the **MPL-2.0 License** (Mozilla Public License 2.0).
302
810
 
811
+ See the [LICENSE](LICENSE) file for details.
303
812
 
304
- #### HealthUnit
813
+ ---
305
814
 
306
- <code>'count' | 'meter' | 'kilocalorie' | 'bpm' | 'kilogram' | 'minute'</code>
815
+ ## 🔗 Links
307
816
 
817
+ - **GitHub Repository**: [https://github.com/sandip-3008/capacitor-health](https://github.com/sandip-3008/capacitor-health)
818
+ - **npm Package**: [@interval-health/capacitor-health](https://www.npmjs.com/package/@interval-health/capacitor-health)
819
+ - **Issues & Bug Reports**: [GitHub Issues](https://github.com/sandip-3008/capacitor-health/issues)
308
820
 
309
- #### SleepState
821
+ ---
310
822
 
311
- <code>'inBed' | 'asleep' | 'awake' | 'asleepCore' | 'asleepDeep' | 'asleepREM' | 'unknown'</code>
823
+ ## 📞 Support
312
824
 
825
+ For questions, issues, or feature requests:
313
826
 
314
- #### Record
827
+ 1. Check the [documentation](#-api-reference) and [common patterns](#-common-usage-patterns)
828
+ 2. Search [existing issues](https://github.com/sandip-3008/capacitor-health/issues)
829
+ 3. Open a new issue with detailed information about your problem
315
830
 
316
- Construct a type with a set of properties K of type T
831
+ **Note**: When reporting issues, please include:
832
+ - Platform (iOS/Android)
833
+ - OS version
834
+ - Plugin version
835
+ - Code sample demonstrating the issue
836
+ - Error messages or logs
317
837
 
318
- <code>{
319
838
  [P in K]: T;
320
839
  }</code>
840
+ ---
321
841
 
322
- </docgen-api>
842
+ ## 📋 TypeScript Types
843
+
844
+ The plugin is written in TypeScript and includes full type definitions. Import types directly:
845
+
846
+ ```typescript
847
+ import {
848
+ Health,
849
+ HealthDataType,
850
+ HealthUnit,
851
+ SleepState,
852
+ AuthorizationOptions,
853
+ AuthorizationStatus,
854
+ QueryOptions,
855
+ HealthSample,
856
+ ReadSamplesResult,
857
+ WriteSampleOptions
858
+ } from '@interval-health/capacitor-health';
859
+ ```
323
860
 
324
- ### Credits:
861
+ ---
325
862
 
326
- this plugin was inspired by the work of https://github.com/perfood/capacitor-healthkit/ for ios and https://github.com/perfood/capacitor-google-fit for Android
863
+ **Made with ❤️ for the Capacitor community**