Skip to content

feat(healthkit): add wasUserEntered filter option to exclude manual entries - #399

Open
michaelyta wants to merge 1 commit into
kingstinct:masterfrom
michaelyta:feat/filter-was-user-entered
Open

michaelyta wants to merge 1 commit into
kingstinct:masterfrom
michaelyta:feat/filter-was-user-entered

Conversation

@michaelyta

@michaelyta michaelyta commented Oct 9, 2026 •

Copy link
Copy Markdown
Contributor

Description

Adds a top-level wasUserEntered?: boolean option to FilterForSamplesBase (which flows through to queryStatisticsForQuantity, queryStatisticsCollectionForQuantity, queryQuantitySamples, and workouts) and exports MetadataKeyWasUserEntered.

When wasUserEntered: false is passed, the native predicate matches samples where HKMetadataKeyWasUserEntered != true, which correctly excludes user-entered samples while including both explicitly non-manual and omitted metadata (such as Apple Watch and iPhone pedometer sensor data).

API Reference

FilterForSamplesBase / FilterForSamples

Property Type Description
wasUserEntered boolean | undefined
  • false: Matches samples not entered manually (HKMetadataKeyWasUserEntered != true), which includes sensor and automated data while omitting manual entries.
  • true: Matches manually entered samples exclusively.
  • undefined / omitted: No filter is applied (default HealthKit behavior).

Supported across queries that accept filter, including:

  • queryStatisticsForQuantity(identifier, options, { filter })
  • queryStatisticsCollectionForQuantity(identifier, options, anchorDate, interval, { filter })
  • queryQuantitySamples(identifier, { filter, ... })
  • queryQuantitySamplesWithAnchor(identifier, { filter, ... })
  • queryWorkoutSamples({ filter, ... })
  • queryWorkoutSamplesWithAnchor({ filter, ... })

Why add this option if generic metadata filter exists?

While filtering out manual entries is technically achievable via:

metadata: {
  withMetadataKey: 'HKMetadataKeyWasUserEntered',
  operatorType: ComparisonPredicateOperator.notEqualTo,
  value: true,
}

having a dedicated wasUserEntered: boolean option is significantly cleaner and more ergonomic:

  1. Discoverability & Developer Experience (DX): Filtering out manual entries in fitness and health apps is an extremely common requirement. Expecting developers to know the exact Apple constant string 'HKMetadataKeyWasUserEntered', know to look for ComparisonPredicateOperator, and realize that it must be notEqualTo: true (rather than equalTo: false, which would return zero results because sensor data omits the key) is unintuitive.
  2. First-class typing: Provides autocomplete and clean type safety right on filter.

(Note: If maintainers feel strongly that metadata filters should exclusively be handled through the generic metadata property rather than dedicated shortcuts, happy to discuss and hear thoughts!)

Testing

Physical Device Testing

  • Tested in a fitness app I'm working on on a physical iOS device with real HealthKit step data:
const statsCollection = await queryStatisticsCollectionForQuantity(
  'HKQuantityTypeIdentifierStepCount',
  ['cumulativeSum'],
  anchorDate,
  { day: 1 },
  {
    filter: {
      date: { startDate: start, endDate: end },
      wasUserEntered: false, // Excludes manually entered steps
    },
  }
);
  • wasUserEntered: false: correctly excluded manually entered steps and kept automatically recorded steps.
  • wasUserEntered: true: exclusively returned the manual entries.
  • Omitting the property: returned all steps (both automatic and manual), matching default HealthKit behavior.

Automated Testing

  • Added unit tests in src/hooks/queryStatisticsForQuantity.test.ts:
    • Verified that wasUserEntered: false is correctly forwarded to the native module when executing queryStatisticsForQuantity.
    • Verified that MetadataKeyWasUserEntered constant exports 'HKMetadataKeyWasUserEntered'.
  • Ran full test suite via bun test (14/14 passing).
  • Ran TypeScript typecheck (tsc --noEmit) covering both core library and type tests.
  • Ran Biome linter & SwiftLint with 0 warnings/errors.

@changeset-bot

changeset-bot Bot commented Oct 9, 2026 •

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: a242ddf

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 3 packages
Name Type
@kingstinct/react-native-healthkit Minor
@react-native-healthkit/core Minor
@react-native-healthkit/health-records Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@pkg-pr-new

pkg-pr-new Bot commented Oct 9, 2026 •

Copy link
Copy Markdown

Open in StackBlitz

npm i https://pkg.pr.new/kingstinct/react-native-healthkit/@react-native-healthkit/core@399
npm i https://pkg.pr.new/kingstinct/react-native-healthkit/@react-native-healthkit/health-records@399
npm i https://pkg.pr.new/kingstinct/react-native-healthkit/@kingstinct/react-native-healthkit@399

commit: a242ddf

@michaelyta
michaelyta force-pushed the feat/filter-was-user-entered branch from b9f1ee3 to a242ddf Compare October 9, 2026 19:18
@michaelyta
michaelyta marked this pull request as ready for review October 9, 2026 19:50

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant