- name
- shiny-health
- description
- Generate cross-platform health data queries, write health data, and observe real-time health changes using Shiny Health for Apple HealthKit and Android Health Connect
- auto_invoke
- true
- triggers
- ["health data","health kit","healthkit","health connect","step count","heart rate","calories","distance","weight","height","body fat","blood pressure","oxygen saturation","sleep duration","hydration","resting heart rate","health metrics","health permissions","IHealthService","DataType","NumericHealthResult","BloodPressureResult","HealthResult","GetStepCounts","GetCalories","GetDistances","GetAverageHeartRate","GetWeight","GetHeight","GetBodyFatPercentage","GetRestingHeartRate","GetBloodPressure","[Truncated]"]
# Shiny Health Skill
You are an expert in Shiny Health, a .NET MAUI library that provides a unified API for reading and writing health data from Apple HealthKit (iOS) and Android Health Connect.
## When to Use This Skill
Invoke this skill when the user wants to:
- Query health metrics (steps, heart rate, calories, distance, weight, height, body fat, blood pressure, oxygen saturation, sleep, hydration)
- Write/log health data (steps, weight, hydration, blood pressure, etc.)
- Observe real-time health data changes (e.g., monitor step counts or heart rate as they are recorded)
- Set up health data access in a .NET MAUI application
- Request health data permissions (read and/or write) on iOS or Android
- Work with time-bucketed health data aggregations
- Understand which health metrics are available cross-platform
- Configure iOS HealthKit entitlements or Android Health Connect permissions
## Library Overview
**GitHub**: https://github.com/shinyorg/health
**NuGet**: `Shiny.Health`
**Namespace**: `Shiny.Health`
Shiny Health provides:
- A single `IHealthService` interface that works on both iOS and Android
- Read and write support for 30+ cross-platform health metrics spanning activity, body, vitals,
nutrition, reproductive/cycle tracking, and workouts
- Real-time observation of health data changes via `IAsyncEnumerable<HealthResult>`
- Time-bucketed aggregate queries at minute, hour, or day intervals
- Permission management with read/write granularity via `PermissionType`
- AOT-compatible implementation (no .NET reflection)
## Setup
### 1. Install NuGet Package
```bash
dotnet add package Shiny.Health
```
### 2. Configure in MauiProgram.cs
```csharp
public static MauiApp CreateMauiApp()
{
var builder = MauiApp
.CreateBuilder()
.UseMauiApp<App>()
.UseShiny();
builder.Services.AddHealthIntegration();
return builder.Build();
}
```
### 3. iOS Setup
Your app requires a provisioning profile with HealthKit capabilities enabled.
**Info.plist:**
```xml
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>healthkit</string>
</array>
<key>NSHealthUpdateUsageDescription</key>
<string>We need access to update your health data</string>
<key>NSHealthShareUsageDescription</key>
<string>We need access to read your health data</string>
```
**Entitlements.plist:**
```xml
<key>com.apple.developer.healthkit</key>
<true />
<key>com.apple.developer.healthkit.background-delivery</key>
<true />
```
### 4. Android Setup (Health Connect)
Android uses Health Connect (the replacement for the deprecated Google Fit API). Health Connect requires Android 9 (API 28) or higher.
**AndroidManifest.xml:**
```xml
<!-- Declare which health data your app reads (only include the types you use) -->
<uses-permission android:name="android.permission.health.READ_STEPS" />
<uses-permission android:name="android.permission.health.READ_HEART_RATE" />
<uses-permission android:name="android.permission.health.READ_TOTAL_CALORIES_BURNED" />
<uses-permission android:name="android.permission.health.READ_DISTANCE" />
<uses-permission android:name="android.permission.health.READ_WEIGHT" />
<uses-permission android:name="android.permission.health.READ_HEIGHT" />
<uses-permission android:name="android.permission.health.READ_BODY_FAT" />
<uses-permission android:name="android.permission.health.READ_RESTING_HEART_RATE" />
<uses-permission android:name="android.permission.health.READ_BLOOD_PRESSURE" />
<uses-permission android:name="android.permission.health.READ_OXYGEN_SATURATION" />
<uses-permission android:name="android.permission.health.READ_SLEEP" />
<uses-permission android:name="android.permission.health.READ_HYDRATION" />
<uses-permission android:name="android.permission.health.READ_MENSTRUATION" />
<uses-permission android:name="android.permission.health.READ_BLOOD_GLUCOSE" />
<uses-permission android:name="android.permission.health.READ_BODY_TEMPERATURE" />
<uses-permission android:name="android.permission.health.READ_BASAL_BODY_TEMPERATURE" />
<uses-permission android:name="android.permission.health.READ_RESPIRATORY_RATE" />
<uses-permission android:name="android.permission.health.READ_VO2_MAX" />
<uses-permission android:name="android.permission.health.READ_HEART_RATE_VARIABILITY" />
<uses-permission android:name="android.permission.health.READ_LEAN_BODY_MASS" />
<uses-permission android:name="android.permission.health.READ_BASAL_METABOLIC_RATE" />
<uses-permission android:name="android.permission.health.READ_ACTIVE_CALORIES_BURNED" />
<uses-permission android:name="android.permission.health.READ_FLOORS_CLIMBED" />
<uses-permission android:name="android.permission.health.READ_WHEELCHAIR_PUSHES" />
<uses-permission android:name="android.permission.health.READ_SPEED" />
<uses-permission android:name="android.permission.health.READ_POWER" />
<uses-permission android:name="android.permission.health.READ_SEXUAL_ACTIVITY" />
<uses-permission android:name="android.permission.health.READ_OVULATION_TEST" />
<uses-permission android:name="android.permission.health.READ_CERVICAL_MUCUS" />
<uses-permission android:name="android.permission.health.READ_INTERMENSTRUAL_BLEEDING" />
<uses-permission android:name="android.permission.health.READ_EXERCISE" />
<uses-permission android:name="android.permission.health.READ_NUTRITION" />
<uses-permission android:name="android.permission.ACTIVITY_RECOGNITION" />
<!-- Optional: declare which health data your app writes (only include the types you need) -->
<uses-permission android:name="android.permission.health.WRITE_STEPS" />
<uses-permission android:name="android.permission.health.WRITE_HEART_RATE" />
<uses-permission android:name="android.permission.health.WRITE_TOTAL_CALORIES_BURNED" />
<uses-permission android:name="android.permission.health.WRITE_DISTANCE" />
<uses-permission android:name="android.permission.health.WRITE_WEIGHT" />
<uses-permission android:name="android.permission.health.WRITE_HEIGHT" />
<uses-permission android:name="android.permission.health.WRITE_BODY_FAT" />
<uses-permission android:name="android.permission.health.WRITE_RESTING_HEART_RATE" />
<uses-permission android:name="android.permission.health.WRITE_BLOOD_PRESSURE" />
<uses-permission android:name="android.permission.health.WRITE_OXYGEN_SATURATION" />
<uses-permission android:name="android.permission.health.WRITE_SLEEP" />
<uses-permission android:name="android.permission.health.WRITE_HYDRATION" />
<uses-permission android:name="android.permission.health.WRITE_MENSTRUATION" />
<uses-permission android:name="android.permission.health.WRITE_BLOOD_GLUCOSE" />
<uses-permission android:name="android.permission.health.WRITE_BODY_TEMPERATURE" />
<uses-permission android:name="android.permission.health.WRITE_BASAL_BODY_TEMPERATURE" />
<uses-permission android:name="android.permission.health.WRITE_RESPIRATORY_RATE" />
<uses-permission android:name="android.permission.health.WRITE_VO2_MAX" />
<uses-permission android:name="android.permission.health.WRITE_HEART_RATE_VARIABILITY" />
<uses-permission android:name="android.permission.health.WRITE_LEAN_BODY_MASS" />
<uses-permission android:name="android.permission.health.WRITE_BASAL_METABOLIC_RATE" />
<uses-permission android:name="android.permission.health.WRITE_ACTIVE_CALORIES_BURNED" />
<uses-permission android:name="android.permission.health.WRITE_FLOORS_CLIMBED" />
<uses-permission android:name="android.permission.health.WRITE_WHEELCHAIR_PUSHES" />
<uses-permission android:name="android.permission.health.WRITE_SPEED" />
<uses-permission android:name="android.permission.health.WRITE_POWER" />
<uses-permission android:name="android.permission.health.WRITE_SEXUAL_ACTIVITY" />
<uses-permission android:name="android.permission.health.WRITE_OVULATION_TEST" />
<uses-permission android:name="android.permission.health.WRITE_CERVICAL_MUCUS" />
<uses-permission android:name="android.permission.health.WRITE_INTERMENSTRUAL_BLEEDING" />
<uses-permission android:name="android.permission.health.WRITE_EXERCISE" />
<uses-permission android:name="android.permission.health.WRITE_NUTRITION" />
<!-- Allow your app to discover Health Connect -->
<queries>
<package android:name="com.google.android.apps.healthdata" />
</queries>
<!-- REQUIRED on Android 14+ (API 34) where Health Connect is part of the platform. Without this
alias Health Connect silently refuses to grant health permissions. android:targetActivity must
match your MainActivity's android:name -->
<application>
<activity-alias
android:name="ViewPermissionUsageActivity"
android:exported="true"
android:targetActivity="com.companyname.myapp.MainActivity"
android:permission="android.permission.START_VIEW_PERMISSION_USAGE">
<intent-filter>
<action android:name="android.intent.action.VIEW_PERMISSION_USAGE" />
<category android:name="android.intent.category.HEALTH_PERMISSIONS" />
</intent-filter>
</activity-alias>
</application>
```
**Platforms/Android/MainActivity.cs** — Health Connect links to your privacy policy from the
permission dialog and will not grant permissions unless the activity handles the rationale action.
Set an explicit `Name` so the `activity-alias` can target it:
```csharp
[Activity(
Name = "com.companyname.myapp.MainActivity",
Theme = "@style/Maui.SplashTheme",
MainLauncher = true,
ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation | ConfigChanges.UiMode | ConfigChanges.ScreenLayout | ConfigChanges.SmallestScreenSize | ConfigChanges.Density
)]
[IntentFilter(["androidx.health.ACTION_SHOW_PERMISSIONS_RATIONALE"])]
public class MainActivity : MauiAppCompatActivity
{
}
```
**Requirements:**
- The Health Connect app must be installed on the device — on Android 14+ (API 34) it is built into the platform
- Minimum SDK version must be set to **28** (Android 9)
- Set `targetSdkVersion` to the platform you build against. An empty `<uses-sdk />` element suppresses
it and falls back to `minSdkVersion`, which recent Android versions block at install time with
"Unsafe app blocked - built for an older version of Android"
- Permission names follow the **Health Connect record**, not the HealthKit type — `DataType.Calories`
reads `TotalCaloriesBurnedRecord` and needs `READ_TOTAL_CALORIES_BURNED`. There is no
`…_TOTAL_ENERGY_BURNED` permission on Android. A name Android does not recognize is silently
unknown: it can never be granted and every read of that type fails
- Never request health permissions with `Permissions.RequestAsync<>` or the AndroidX activity-result
contracts directly — always go through `IHealthService.RequestPermissions`, which picks the right
flow for the OS version
## API Reference
### Core Types
```csharp
// Permission type for read/write access
[Flags]
public enum PermissionType
{
Read = 1,
Write = 2,
ReadWrite = Read | Write
}
// Time interval for bucketed queries
public enum Interval { Minutes, Hours, Days }
// Available health data types
public enum DataType
{
// numeric (NumericHealthResult)
StepCount, HeartRate, Calories, Distance,
Weight, Height, BodyFatPercentage, RestingHeartRate,
BloodPressure, OxygenSaturation, SleepDuration, Hydration,
BloodGlucose, BodyTemperature, BasalBodyTemperature, RespiratoryRate,
Vo2Max, HeartRateVariability, LeanBodyMass, BasalEnergyBurned,
ActiveEnergyBurned, FloorsClimbed, WheelchairPushes, Speed, Power,
// categorical / event-based
MenstruationFlow, SexualActivity, OvulationTest, CervicalMucus, IntermenstrualBleeding,
// structured records
Workout, Nutrition
}
// Menstrual flow level (categorical). None is iOS-only; Android maps it to Unspecified.
public enum MenstrualFlow { Unspecified, None, Light, Medium, Heavy }
// Reproductive / cycle-tracking enums
public enum SexualActivityProtection { Unspecified, Protected, Unprotected }
public enum OvulationTestOutcome { Inconclusive, Positive, High, Negative }
public enum CervicalMucusAppearance { Unspecified, Dry, Sticky, Creamy, Watery, EggWhite }
// Workout activity (subset mapped on both platforms; unmapped -> Other) and meal type
public enum WorkoutType { Other, Running, Walking, Hiking, Cycling, Swimming, Rowing, Elliptical,
StairClimbing, StrengthTraining, HighIntensityIntervalTraining, Yoga, Pilates, Tennis,
Basketball, Soccer, Baseball, Golf, Boxing, MartialArts, Dancing }
public enum MealType { Unknown, Breakfast, Lunch, Dinner, Snack }
// Categorical / structured result records
public record SexualActivityResult(DateTimeOffset Start, DateTimeOffset End, SexualActivityProtection Protection) : HealthResult(DataType.SexualActivity, Start, End);
public record OvulationTestResult(DateTimeOffset Start, DateTimeOffset End, OvulationTestOutcome Outcome) : HealthResult(DataType.OvulationTest, Start, End);
public record CervicalMucusResult(DateTimeOffset Start, DateTimeOffset End, CervicalMucusAppearance Appearance) : HealthResult(DataType.CervicalMucus, Start, End);
public record IntermenstrualBleedingResult(DateTimeOffset Start, DateTimeOffset End) : HealthResult(DataType.IntermenstrualBleeding, Start, End);
public record WorkoutResult(DateTimeOffset Start, DateTimeOffset End, WorkoutType Workout, double? TotalEnergyKilocalories = null, double? TotalDistanceMeters = null, string? Title = null) : HealthResult(DataType.Workout, Start, End);
public record NutritionResult(DateTimeOffset Start, DateTimeOffset End, MealType Meal = MealType.Unknown, string? Name = null, double? EnergyKilocalories = null, double? ProteinGrams = null, double? CarbohydratesGrams = null, double? TotalFatGrams = null, double? FiberGrams = null, double? SugarGrams = null, double? SodiumGrams = null, double? CholesterolGrams = null) : HealthResult(DataType.Nutrition, Start, End);
// Result for single-value metrics
public record NumericHealthResult(
DataType DataType,
DateTimeOffset Start,
DateTimeOffset End,
double Value
) : HealthResult(DataType, Start, End);
// Result for blood pressure (dual-value)
public record BloodPressureResult(
DateTimeOffset Start,
DateTimeOffset End,
double Systolic,
double Diastolic
) : HealthResult(DataType.BloodPressure, Start, End);
// Result for menstruation flow (categorical, event-based)
// IsCycleStart marks the first day of the cycle (iOS only; always false on Android)
public record MenstruationFlowResult(
DateTimeOffset Start,
DateTimeOffset End,
MenstrualFlow Flow,
bool IsCycleStart = false
) : HealthResult(DataType.MenstruationFlow, Start, End);
```
### IHealthService Interface
```csharp
public interface IHealthService
{
// True if the platform health store is usable (iOS: always; Android: Health Connect installed & SDK OK)
bool IsAvailable { get; }
// Observe real-time health data changes (forward-only, yields new samples as recorded)
// iOS: push-based HKAnchoredObjectQuery; Android: polls Health Connect change tokens
View on GitHub