@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 +730 -193
- package/android/src/main/java/app/capgo/plugin/health/HealthDataType.kt +2 -1
- package/dist/docs.json +20 -0
- package/dist/esm/definitions.d.ts +2 -2
- package/dist/esm/definitions.js.map +1 -1
- package/ios/Sources/HealthPlugin/Health.swift +1598 -57
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,324 +1,861 @@
|
|
|
1
|
-
# @
|
|
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
|
-
|
|
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
|
-
|
|
5
|
+
---
|
|
10
6
|
|
|
11
|
-
##
|
|
7
|
+
## 📦 Installation
|
|
12
8
|
|
|
13
|
-
|
|
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
|
-
|
|
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
|
-
|
|
21
|
+
## 🚀 Supported Platforms
|
|
24
22
|
|
|
25
|
-
|
|
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
|
-
|
|
29
|
+
### Platform Requirements
|
|
28
30
|
|
|
29
|
-
|
|
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
|
-
|
|
32
|
-
|
|
33
|
-
|
|
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
|
-
|
|
42
|
+
---
|
|
37
43
|
|
|
38
|
-
|
|
39
|
-
|
|
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
|
|
52
|
+
<string>This app needs access to read your health data.</string>
|
|
44
53
|
<key>NSHealthUpdateUsageDescription</key>
|
|
45
|
-
<string>This app
|
|
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
|
-
|
|
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
|
-
|
|
218
|
+
Check if health services are available on the current platform.
|
|
51
219
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
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
|
-
|
|
224
|
+
**Returns**: `Promise<AvailabilityResult>`
|
|
58
225
|
|
|
59
|
-
|
|
226
|
+
**Example**:
|
|
227
|
+
```typescript
|
|
228
|
+
const result = await Health.isAvailable();
|
|
60
229
|
|
|
61
|
-
|
|
62
|
-
|
|
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
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
237
|
+
**Response Example**:
|
|
238
|
+
```typescript
|
|
239
|
+
{
|
|
240
|
+
available: true,
|
|
241
|
+
platform: 'ios' // or 'android'
|
|
68
242
|
}
|
|
69
243
|
|
|
70
|
-
//
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
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
|
-
|
|
77
|
-
|
|
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
|
-
|
|
85
|
-
|
|
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
|
-
|
|
91
|
-
|
|
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:
|
|
94
|
-
endDate:
|
|
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
|
-
|
|
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
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
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
|
-
|
|
417
|
+
---
|
|
111
418
|
|
|
112
|
-
|
|
419
|
+
### saveSample()
|
|
113
420
|
|
|
114
|
-
|
|
421
|
+
Write a single health sample to the native health store.
|
|
115
422
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
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
|
-
|
|
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
|
-
<
|
|
128
|
-
<!--Update the source file JSDoc comments and rerun docgen to update the docs below-->
|
|
435
|
+
**Returns**: `Promise<void>`
|
|
129
436
|
|
|
130
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
**
|
|
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
|
-
|
|
484
|
+
Get the current version of the native plugin.
|
|
144
485
|
|
|
145
486
|
```typescript
|
|
146
|
-
|
|
487
|
+
Health.getPluginVersion(): Promise<{ version: string }>
|
|
147
488
|
```
|
|
148
489
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
| Param | Type |
|
|
152
|
-
| ------------- | --------------------------------------------------------------------- |
|
|
153
|
-
| **`options`** | <code><a href="#authorizationoptions">AuthorizationOptions</a></code> |
|
|
490
|
+
**Returns**: `Promise<{ version: string }>`
|
|
154
491
|
|
|
155
|
-
**
|
|
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
|
-
###
|
|
502
|
+
### 1. Displaying Daily Step Count
|
|
161
503
|
|
|
162
504
|
```typescript
|
|
163
|
-
|
|
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
|
-
|
|
167
|
-
|
|
168
|
-
| Param | Type |
|
|
169
|
-
| ------------- | --------------------------------------------------------------------- |
|
|
170
|
-
| **`options`** | <code><a href="#authorizationoptions">AuthorizationOptions</a></code> |
|
|
522
|
+
### 2. Weekly Activity Summary
|
|
171
523
|
|
|
172
|
-
|
|
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
|
-
###
|
|
574
|
+
### 4. Sleep Analysis
|
|
178
575
|
|
|
179
576
|
```typescript
|
|
180
|
-
|
|
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
|
-
|
|
606
|
+
### 5. Recording a Workout
|
|
184
607
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
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
|
-
###
|
|
643
|
+
### Common Errors and Solutions
|
|
195
644
|
|
|
196
645
|
```typescript
|
|
197
|
-
|
|
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
|
-
|
|
690
|
+
### Error Types
|
|
201
691
|
|
|
202
|
-
|
|
|
203
|
-
|
|
204
|
-
|
|
|
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
|
-
###
|
|
707
|
+
### iOS Privacy Considerations
|
|
210
708
|
|
|
211
|
-
|
|
212
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
731
|
+
## 🐛 Known Limitations & Issues
|
|
223
732
|
|
|
733
|
+
### iOS Limitations
|
|
224
734
|
|
|
225
|
-
|
|
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
|
-
|
|
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
|
-
|
|
747
|
+
### General Limitations
|
|
235
748
|
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
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
|
-
|
|
757
|
+
## 📱 Platform-Specific Notes
|
|
245
758
|
|
|
246
|
-
|
|
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
|
-
|
|
766
|
+
### Android (Health Connect)
|
|
253
767
|
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
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
|
-
|
|
776
|
+
## 🧪 Testing
|
|
260
777
|
|
|
261
|
-
|
|
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
|
-
|
|
784
|
+
### Testing on Android
|
|
274
785
|
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
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
|
-
|
|
793
|
+
## 🤝 Contributing
|
|
285
794
|
|
|
286
|
-
|
|
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><string, string></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
|
-
|
|
803
|
+
Please ensure your code follows the existing code style and includes appropriate error handling.
|
|
297
804
|
|
|
805
|
+
---
|
|
298
806
|
|
|
299
|
-
|
|
807
|
+
## 📄 License
|
|
300
808
|
|
|
301
|
-
|
|
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
|
-
|
|
813
|
+
---
|
|
305
814
|
|
|
306
|
-
|
|
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
|
-
|
|
821
|
+
---
|
|
310
822
|
|
|
311
|
-
|
|
823
|
+
## 📞 Support
|
|
312
824
|
|
|
825
|
+
For questions, issues, or feature requests:
|
|
313
826
|
|
|
314
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
861
|
+
---
|
|
325
862
|
|
|
326
|
-
|
|
863
|
+
**Made with ❤️ for the Capacitor community**
|