Skip to content
Shiny Controls v1.0 - The Ultra Control Suite for .NET MAUI & BlazorO...M...G!

Platform Notes

  • HealthKit requires a real device (not simulator) for most data types
  • RequestPermissions on iOS does NOT tell you if the user denied access (Apple privacy policy) — it may return true even when denied
  • Percentage values (body fat, O2 saturation) are returned as 0–100, not 0–1
  • Your app requires a provisioning profile with HealthKit capabilities enabled
  • Observe uses HKAnchoredObjectQuery for push-based real-time delivery — no polling needed
  • Menstruation flow uses HKCategoryType.MenstrualFlow; the MenstruationFlowResult.IsCycleStart flag maps to the HKMetadataKeyMenstrualCycleStart sample metadata, and the MenstrualFlow.None level is supported
  • Heart rate variability uses HKQuantityTypeIdentifier.HeartRateVariabilitySDNN (SDNN, in ms)
  • Speed maps to WalkingSpeed and Power maps to CyclingPower (HealthKit has no generic speed/power types)
  • Workouts use HKWorkout with WorkoutType mapped to HKWorkoutActivityType; nutrition is written as an HKCorrelation of type Food containing the individual dietary quantity samples
  • Sexual activity protection is stored via the HKMetadataKeySexualActivityProtectionUsed metadata key
  • The Health Connect app must be installed on the device — on Android 14+ (API 34) it ships as part of the platform
  • Minimum SDK version must be 28 (Android 9) or higher
  • Set targetSdkVersion to the platform you build against. An empty <uses-sdk /> element in AndroidManifest.xml suppresses it and falls back to minSdkVersion — recent Android versions then block the install with “Unsafe app blocked — this app was built for an older version of Android”
  • The permission flow differs by OS version and RequestPermissions handles both: on Android 14+ the health permissions are ordinary runtime permissions, while on earlier versions they belong to the Health Connect APK and are granted through its own permission activity. Never request them yourself with Permissions.RequestAsync<> or the AndroidX activity-result contracts
  • Android 14+ additionally requires a ViewPermissionUsageActivity activity-alias (with the android.intent.category.HEALTH_PERMISSIONS category) plus an androidx.health.ACTION_SHOW_PERMISSIONS_RATIONALE intent filter on your MainActivity. Without them Health Connect refuses to grant health permissions — the Setup builder emits both
  • Body fat percentage and oxygen saturation use individual record queries (Health Connect does not provide aggregate metrics for these types)
  • Observe uses Health Connect change tokens with polling (default 5 seconds) — Health Connect does not support push-based observation
  • Menstruation flow uses the MenstruationFlowRecord with the android.permission.health.READ_MENSTRUATION / WRITE_MENSTRUATION permissions. There is no None flow value (written as Unspecified), and IsCycleStart is ignored — Health Connect models the period span as a separate MenstruationPeriodRecord
  • Heart rate variability uses HeartRateVariabilityRmssdRecord (RMSSD, in ms) — a different computation than iOS’s SDNN, so the values are not directly comparable
  • Each new type needs its own permission, e.g. READ_BLOOD_GLUCOSE, READ_VO2_MAX, READ_EXERCISE (workouts), READ_NUTRITION, READ_SEXUAL_ACTIVITY (and the WRITE_… equivalents) — declare them in AndroidManifest.xml
  • Permission names follow the Health Connect record, not the HealthKit type. DataType.Calories reads TotalCaloriesBurnedRecord, so it needs READ_TOTAL_CALORIES_BURNED — there is no …_TOTAL_ENERGY_BURNED permission on Android even though HealthKit calls the equivalent type ActiveEnergyBurned. A permission the platform does not recognize is silently unknown: it can never be granted, and every read of that type fails
  • Health Connect lets the user grant a subset of what you asked for, so treat a partial grant as the normal case: check the per-DataType results from RequestPermissions and only query what came back true. Querying a type whose permission was not granted throws — it does not return an empty list
  • Instantaneous records without aggregate metrics (blood glucose, body/basal temperature, respiratory rate, VO2 max, HRV, lean body mass) are read with ReadRecords and bucketed in-memory, like body fat and oxygen saturation
  • Workouts use ExerciseSessionRecord; its WorkoutResult energy and distance are null on read because Health Connect stores those as separate ActiveCaloriesBurnedRecord / DistanceRecord entries
  • BasalEnergyBurned is derived from BasalMetabolicRateRecord (a rate); on write the value is treated as kcal/day