health_connector_hk_ios


📖 Overview

health_connector_hk_ios is the iOS platform implementation for the Health Connector plugin. It provides integration with Apple's HealthKit framework, enabling Flutter apps to read, write, and aggregate health data on iOS devices.

✨ Features

Feature Description
🔐 Permission Management Request read/write permissions for health data types
📖 Reading Health Data Read a single health record by ID or multiple health records within a date/time range with pagination
✍️ Writing Health Data Write health records with metadata
🔄 Updating Health Records Modify existing records (delete-then-insert pattern)
🗑️ Deleting Health Records Remove specific records by their IDs or within a date/time range
Aggregating Health Data Sum/Avg/Min/Max Aggregation

🎯 Requirements

  • Flutter >=3.3.0
  • Dart >=3.9.2
  • iOS >=15.0
  • Xcode >=14.0

🤔 Why iOS 15.0?

Although Swift's concurrency features can be back-deployed to iOS 13.0+ using Xcode 13.2+, we intentionally set iOS 15.0 as the minimum supported version to ensure:

  • Full access to the native Swift concurrency runtime without back-deployment shims
  • A simpler and more reliable runtime environment with no compatibility layers
  • Better long-term maintainability and reduced technical debt

Note: HealthKit itself has been available since iOS 8.0. The iOS 15.0 requirement is a conscious architectural decision driven by native concurrency support - not a limitation of HealthKit.


🚀 Getting Started

📦 Installation

Add to your pubspec.yaml:

flutter pub add health_connector_hk_ios

Or manually add:

dependencies:
  health_connector_hk_ios: [latest_version]

⚙️ iOS Setup

Enable HealthKit Capability

Open your project in Xcode (ios/Runner.xcworkspace) and:

  1. Select your app target
  2. Go to Signing & Capabilities tab
  3. Click + Capability
  4. Add HealthKit
Update Info.plist

Add usage descriptions to ios/Runner/Info.plist:

<dict>
    <!-- Existing keys -->

    <!-- HealthKit usage description -->
    <key>NSHealthShareUsageDescription</key>
    <string>This app needs to read your health data to provide personalized insights.</string>

    <key>NSHealthUpdateUsageDescription</key>
    <string>This app needs to save health data to track your progress.</string>

    <!-- Optional: For background delivery -->
    <key>UIBackgroundModes</key>
    <array>
        <string>processing</string>
    </array>
</dict>

Important: Provide clear, user-friendly descriptions. Vague descriptions may result in App Store rejection.

📚 Usage

Import Package

import 'package:health_connector/health_connector.dart';

Create HealthConnectorHKClient Instance

final client = HealthConnectorHKClient();

Check Platform Availability

final status = await client.getHealthPlatformStatus();

switch (status) {
  case HealthPlatformStatus.available:
    print('HealthKit ready');
    break;
  case HealthPlatformStatus.unavailable:
    print('HealthKit not supported on this device');
    break;
  case HealthPlatformStatus.installationOrUpdateRequired:
    print('Please install or update Apple Health');
    break;
}

Check Health Platform Feature Status

Note: On iOS HealthKit, all health platform features are available by default. The getFeatureStatus() always returns HealthPlatformFeatureStatus.available for all features.

// Feature status check - always returns available on iOS
final status = await client.getFeatureStatus(
  HealthPlatformFeature.readHealthDataInBackground,
);

// status == HealthPlatformFeatureStatus.available

Permissions

Note:

  • On iOS HealthKit, all health platform features are granted by default. Requesting feature permissions always returns PermissionStatus.granted.
  • Read permissions always return PermissionStatus.unknown for privacy reasons.
Request Permissions
final permissions = [
  HealthDataType.steps.readPermission,
  HealthDataType.steps.writePermission,
  HealthDataType.weight.readPermission,
  HealthDataType.weight.writePermission,
  // ...
];

final results = await client.requestPermissions(permissions);

for (final result in results) {
  print('${result.permission}: ${result.status}');
}

Read Health Records

Read Health Record by ID
final recordId = HealthRecordId('existing-record-id');
final readRequest = HealthDataType.steps.readRecord(recordId);
final record = await client.readRecord(readRequest);

if (record != null) {
  print('Steps: ${record.count.value} from ${record.startTime} to ${record.endTime}');
} else {
  print('Record not found');
}
Read Multiple Health Records
final request = HealthDataType.steps.readRecords(
  startTime: DateTime.now().subtract(Duration(days: 7)),
  endTime: DateTime.now(),
  pageSize: 100,
);

final response = await client.readRecords(request);

for (final record in response.records) {
  print('Steps: ${record.count.value} from ${record.startTime} to ${record.endTime}');
}
Read Multiple Health Records with Pagination
var request = HealthDataType.steps.readRecords(
  startTime: DateTime.now().subtract(Duration(days: 30)),
  endTime: DateTime.now(),
  pageSize: 100,
);

var allRecords = <StepRecord>[];
var pageNumber = 1;

while (true) {
  final response = await client.readRecords(request);
  
  allRecords.addAll(response.records.cast<StepRecord>());
  
  if (response.nextPageRequest == null || response.records.isEmpty) {
    break;
  }
  
  request = response.nextPageRequest!;
  pageNumber++;
}

print('Total records fetched: ${allRecords.length}');

Write Health Records

Write Single Health Record
final stepRecord = StepRecord(
  id: HealthRecordId.none,
  startTime: DateTime.now().subtract(Duration(hours: 1)),
  endTime: DateTime.now(),
  count: Numeric(5000),
  metadata: Metadata.automaticallyRecorded(
    device: Device.fromType(DeviceType.phone),
  ),
);

final recordId = await client.writeRecord(stepRecord);
print('Record written with ID: $recordId');
Write Multiple Health Records
final records = [
  StepRecord(
    id: HealthRecordId.none,
    startTime: DateTime.now().subtract(Duration(hours: 2)),
    endTime: DateTime.now().subtract(Duration(hours: 1)),
    count: Numeric(3000),
    metadata: Metadata.automaticallyRecorded(
      device: Device.fromType(DeviceType.phone),
    ),
  ),
  DistanceRecord(
    id: HealthRecordId.none,
    startTime: DateTime.now().subtract(Duration(hours: 1)),
    endTime: DateTime.now(),
    distance: Length(5000),
    metadata: Metadata.automaticallyRecorded(
      device: Device.fromType(DeviceType.phone),
    ),
  ),
  // ...
];

final recordIds = await client.writeRecords(records);
print('Wrote ${recordIds.length} records');

Update Health Records

final recordId = HealthRecordId('existing-record-id');
final readRequest = HealthDataType.steps.readRecord(recordId);
final existingRecord = await client.readRecord(readRequest);

if (existingRecord != null) {
  final originalId = existingRecord.id;
  
  final updatedRecord = existingRecord.copyWith(
    startTime: existingRecord.startTime,
    endTime: existingRecord.endTime,
    count: Numeric(existingRecord.count.value + 100),
    metadata: existingRecord.metadata,
  );

  final newId = await client.updateRecord(updatedRecord);
  
  print('Original ID: $originalId');
  print('New ID: $newId');
  // originalId != newId on iOS
}

Important: HealthKit uses an immutable data model. Existing samples cannot be modified after being saved. The plugin implements updates as delete-then-insert, so the record ID changes after update (new UUID assigned).

Delete Health Records

Delete Health Records by IDs
await client.deleteRecordsByIds(
  dataType: HealthDataType.steps,
  recordIds: [
    HealthRecordId('id-1'),
    HealthRecordId('id-2'),
  ],
);

print('Records deleted');
Delete Health Records by Date Range
await client.deleteRecords(
  dataType: HealthDataType.steps,
  startTime: DateTime.now().subtract(Duration(days: 7)),
  endTime: DateTime.now(),
);

print('Records deleted');

Aggregate Health Data

final sumRequest = HealthDataType.steps.aggregateSum(
  startTime: DateTime.now().startOfDay,
  endTime: DateTime.now(),
);

final sumResponse = await client.aggregate(sumRequest);
print('Total steps today: ${sumResponse.value.value}');

final avgRequest = HealthDataType.weight.aggregateAvg(
  startTime: DateTime.now().subtract(Duration(days: 30)),
  endTime: DateTime.now(),
);

final avgResponse = await client.aggregate(avgRequest);
print('Average weight: ${avgResponse.value.inKilograms} kg');

final minRequest = HealthDataType.weight.aggregateMin(
  startTime: DateTime.now().subtract(Duration(days: 30)),
  endTime: DateTime.now(),
);
final minResponse = await client.aggregate(minRequest);
print('Min weight: ${minResponse.value.inKilograms} kg');

final maxRequest = HealthDataType.weight.aggregateMax(
  startTime: DateTime.now().subtract(Duration(days: 30)),
  endTime: DateTime.now(),
);
final maxResponse = await client.aggregate(maxRequest);
print('Max weight: ${maxResponse.value.inKilograms} kg');

📋 Supported Health Data Types

Note: For a complete list of all HealthKit data types, see the official HealthKit documentation.

🏃 Activity

Data Type Supported Documentation
Steps HKQuantityTypeIdentifier.stepCount
Distance HKQuantityTypeIdentifier.distanceWalkingRunning
Active Calories Burned HKQuantityTypeIdentifier.activeEnergyBurned
Floors Climbed HKQuantityTypeIdentifier.flightsClimbed
Wheelchair Pushes HKQuantityTypeIdentifier.pushCount
Workout HKWorkoutTypeIdentifier
Total Calories Burned HKQuantityTypeIdentifier.basalEnergyBurned
Power HKQuantityTypeIdentifier.cyclingPower
Speed HKQuantityTypeIdentifier.walkingSpeed, HKQuantityTypeIdentifier.runningSpeed
Distance Cycling HKQuantityTypeIdentifier.distanceCycling
Distance Swimming HKQuantityTypeIdentifier.distanceSwimming
Distance Wheelchair HKQuantityTypeIdentifier.distanceWheelchair
Apple Exercise Time HKQuantityTypeIdentifier.appleExerciseTime
Apple Stand Time HKQuantityTypeIdentifier.appleStandTime
Nike Fuel HKQuantityTypeIdentifier.nikeFuel
Swimming Stroke Count HKQuantityTypeIdentifier.swimmingStrokeCount

📏 Body Measurements

Data Type Supported Documentation
Weight HKQuantityTypeIdentifier.bodyMass
Height HKQuantityTypeIdentifier.height
Body Fat Percentage HKQuantityTypeIdentifier.bodyFatPercentage
Lean Body Mass HKQuantityTypeIdentifier.leanBodyMass
Body Mass Index HKQuantityTypeIdentifier.bodyMassIndex
Waist Circumference HKQuantityTypeIdentifier.waistCircumference

🩸 Cycle Tracking / Reproductive Health

Data Type Supported Documentation
Menstruation Flow HKCategoryTypeIdentifier.menstrualFlow
Cervical Mucus HKCategoryTypeIdentifier.cervicalMucusQuality
Ovulation Test HKCategoryTypeIdentifier.ovulationTestResult
Basal Body Temperature HKQuantityTypeIdentifier.basalBodyTemperature
Sexual Activity HKCategoryTypeIdentifier.sexualActivity
Intermenstrual Bleeding HKCategoryTypeIdentifier.intermenstrualBleeding
Pregnancy Test Result HKCategoryTypeIdentifier.pregnancyTestResult
Progesterone Test Result HKCategoryTypeIdentifier.progesteroneTestResult

🍎 Nutrition

Data Type Supported Documentation
Hydration / Water HKQuantityTypeIdentifier.dietaryWater
Nutrition / Dietary Energy HKQuantityTypeIdentifier.dietaryEnergyConsumed
Macronutrients HKQuantityTypeIdentifier.dietaryProtein, HKQuantityTypeIdentifier.dietaryCarbohydrates, etc.
Vitamins & Minerals Multiple identifiers (Vitamin C, D, Iron, Calcium, etc.)
Dietary Protein HKQuantityTypeIdentifier.dietaryProtein
Dietary Carbohydrates HKQuantityTypeIdentifier.dietaryCarbohydrates
Dietary Fat Total HKQuantityTypeIdentifier.dietaryFatTotal
Dietary Fat Saturated HKQuantityTypeIdentifier.dietaryFatSaturated
Dietary Fat Polyunsaturated HKQuantityTypeIdentifier.dietaryFatPolyunsaturated
Dietary Fat Monounsaturated HKQuantityTypeIdentifier.dietaryFatMonounsaturated
Dietary Cholesterol HKQuantityTypeIdentifier.dietaryCholesterol
Dietary Sodium HKQuantityTypeIdentifier.dietarySodium
Dietary Fiber HKQuantityTypeIdentifier.dietaryFiber
Dietary Sugar HKQuantityTypeIdentifier.dietarySugar
Dietary Calcium HKQuantityTypeIdentifier.dietaryCalcium
Dietary Iron HKQuantityTypeIdentifier.dietaryIron
Dietary Vitamin C HKQuantityTypeIdentifier.dietaryVitaminC
Dietary Vitamin D HKQuantityTypeIdentifier.dietaryVitaminD
Dietary Vitamin E HKQuantityTypeIdentifier.dietaryVitaminE
Dietary Vitamin K HKQuantityTypeIdentifier.dietaryVitaminK
Dietary Vitamin B6 HKQuantityTypeIdentifier.dietaryVitaminB6
Dietary Vitamin B12 HKQuantityTypeIdentifier.dietaryVitaminB12
Dietary Thiamin (B1) HKQuantityTypeIdentifier.dietaryThiamin
Dietary Riboflavin (B2) HKQuantityTypeIdentifier.dietaryRiboflavin
Dietary Niacin (B3) HKQuantityTypeIdentifier.dietaryNiacin
Dietary Folate HKQuantityTypeIdentifier.dietaryFolate
Dietary Biotin HKQuantityTypeIdentifier.dietaryBiotin
Dietary Pantothenic Acid HKQuantityTypeIdentifier.dietaryPantothenicAcid
Caffeine HKQuantityTypeIdentifier.dietaryCaffeine

😴 Sleep

Data Type Supported Documentation
Sleep Stage HKCategoryTypeIdentifier.sleepAnalysis

❤️ Vitals

Data Type Supported Documentation
Heart Rate (Measurement) HKQuantityTypeIdentifier.heartRate
Resting Heart Rate HKQuantityTypeIdentifier.restingHeartRate
Blood Pressure HKQuantityTypeIdentifier.bloodPressureSystolic, HKQuantityTypeIdentifier.bloodPressureDiastolic
Body Temperature HKQuantityTypeIdentifier.bodyTemperature
Oxygen Saturation HKQuantityTypeIdentifier.oxygenSaturation
Respiratory Rate HKQuantityTypeIdentifier.respiratoryRate
Vo2 Max HKQuantityTypeIdentifier.vo2Max
Blood Glucose HKQuantityTypeIdentifier.bloodGlucose
Walking Heart Rate Average HKQuantityTypeIdentifier.walkingHeartRateAverage
Heart Rate Variability SDNN HKQuantityTypeIdentifier.heartRateVariabilitySDNN
Peripheral Perfusion Index HKQuantityTypeIdentifier.peripheralPerfusionIndex
Blood Alcohol Content HKQuantityTypeIdentifier.bloodAlcoholContent
Forced Vital Capacity HKQuantityTypeIdentifier.forcedVitalCapacity
Forced Expiratory Volume HKQuantityTypeIdentifier.forcedExpiratoryVolume1
Peak Expiratory Flow Rate HKQuantityTypeIdentifier.peakExpiratoryFlowRate

🧘 Wellness / Mental Health

Data Type Supported Documentation
Mindfulness Session HKCategoryTypeIdentifier.mindfulSession

🏥 Clinical Records

Data Type Supported Documentation
Electrocardiogram (ECG) HKElectrocardiogram
Vision Prescription HKVisionPrescription

🎧 Audio Exposure

Data Type Supported Documentation
Headphone Audio Exposure HKQuantityTypeIdentifier.headphoneAudioExposureEvent
Environmental Audio Exposure HKQuantityTypeIdentifier.environmentalAudioExposureEvent

🚶 Mobility & Gait Analysis

Data Type Supported Documentation
Walking Speed HKQuantityTypeIdentifier.walkingSpeed
Running Speed HKQuantityTypeIdentifier.runningSpeed
Step Length HKQuantityTypeIdentifier.stepLength
Walking Asymmetry Percentage HKQuantityTypeIdentifier.walkingAsymmetryPercentage
Walking Double Support Percentage HKQuantityTypeIdentifier.walkingDoubleSupportPercentage
Six Minute Walk Test Distance HKQuantityTypeIdentifier.sixMinuteWalkTestDistance
Stair Ascent Speed HKQuantityTypeIdentifier.stairAscentSpeed
Stair Descent Speed HKQuantityTypeIdentifier.stairDescentSpeed

🤝 Contributing

Contributions are welcome!

To report issues or request features, please visit our GitHub Issues.