---
title: "HKStatisticsQuery: one sum, average or extreme over a window"
canonical: "https://aifitnessapi.com/healthkit-queries/hkstatisticsquery"
cluster: "HealthKit Queries"
primary_query: "HKStatisticsQuery"
last_reviewed: "2026-10-03"
description: "HKStatisticsQuery per Apple's docs: cumulative vs discrete options, the accessor each option needs, separateBySource, and why sumQuantity() comes back nil."
publisher: "AIFitnessAPI — independent, not sponsored"
cite_as: "\"HKStatisticsQuery: one sum, average or extreme over a window\", AIFitnessAPI, https://aifitnessapi.com/healthkit-queries/hkstatisticsquery"
---

# HKStatisticsQuery: one sum, average or extreme over a window

> HKStatisticsQuery calculates one statistic over every quantity sample that matches a predicate: a sum for cumulative types such as steps, or a minimum, maximum, average or most recent value for discrete types such as heart rate. Apple documents that it works on quantity samples only, that a discrete option cannot be combined with a cumulative one, and that you must read the result with the accessor matching the option you asked for, so sumQuantity() returns nil unless the query used cumulativeSum. By default HealthKit merges data from all sources before calculating; the separateBySource option gives per-source values as well.

- Canonical: https://aifitnessapi.com/healthkit-queries/hkstatisticsquery
- Last reviewed: 2026-10-03
- Publisher: AIFitnessAPI (https://aifitnessapi.com) — independent, not sponsored
- Cite as: "HKStatisticsQuery: one sum, average or extreme over a window", AIFitnessAPI, https://aifitnessapi.com/healthkit-queries/hkstatisticsquery

---

`HKStatisticsQuery` is the right way to ask HealthKit "how many steps today" or "what was the average heart rate this morning", and the wrong options produce a `nil` with no error attached. This page covers the class as Apple documents it. For a value per day or per hour, the class you want is [HKStatisticsCollectionQuery](/healthkit-queries/hkstatisticscollectionquery).

## What Apple says it is

The abstract: "A query that performs statistical calculations over a set of matching quantity samples, and returns the results." The overview spells out the split this whole page turns on: "You can use statistical queries to calculate the minimum, maximum, or average value of a set of discrete quantities, or use them to calculate the sum for cumulative quantities."

Two limits from the same page:

- "You can use statistics queries with quantity samples only. If you want to calculate statistics over workouts or correlation samples, you must perform the appropriate query and process the data yourself."
- "Statistics queries are immutable. Their properties are set when they are first created, and they can't change."

**Availability.** Apple's class page lists iOS, iPadOS, Mac Catalyst, macOS, visionOS and watchOS with no introduction version in its documentation data. The result type, `HKStatistics`, is listed from iOS 8.0, iPadOS 8.0, Mac Catalyst 13.0, macOS 13.0, visionOS 1.0 and watchOS 2.0.

## The initializer

`init(quantityType:quantitySamplePredicate:options:completionHandler:)` takes:

- `quantityType`: "This type must be an instance of the HKQuantityType class. You cannot perform statistics queries using other sample types."
- `quantitySamplePredicate`: optional; `nil` calculates over every sample of the type.
- `options`: an `HKStatisticsOptions` set. The declaration defaults it to an empty set, and Apple does not describe what a query with no option computes, so always pass one.
- `completionHandler`: called with the query, an `HKStatistics` object ("or nil if an error occurs") and an error.

Apple adds a warning in the discussion: "Statistical calculations can take a considerable amount of time, especially if there are a large number of samples involved."

## Options and the accessor each one needs

Apple's `HKStatistics` page states the rule: "When requesting data from a statistics object, your request must match the options you used when creating the query. For example, if you create a query using the discreteAverage option, you must access the results using the averageQuantity() method." For the sum it is spelled out on `sumQuantity()` itself: "If the cumulativeSum option was set, this method returns a quantity object containing the sum of all the samples matching the query; otherwise, it returns nil."

| Option | Apple's description | Read it with | Listed from |
| --- | --- | --- | --- |
| `cumulativeSum` | "calculates the sum of all the quantities for the samples" | `sumQuantity()` | |
| `discreteAverage` | "calculates the average quantity for the samples" | `averageQuantity()` | |
| `discreteMin` | "calculates the minimum quantity for the samples" | `minimumQuantity()` | |
| `discreteMax` | "calculates the maximum quantity for the samples" | `maximumQuantity()` | |
| `mostRecent` | "returns the most recent quantity from the matching samples" | `mostRecentQuantity()` | iOS 13.0, watchOS 6.0 |
| `duration` | "calculates the total duration covering all the samples" | `duration()` | iOS 13.0, watchOS 6.0 |
| `separateBySource` | "calculates the specified statistics separately for each source" | the `(for:)` variant of each accessor | |

`discreteMostRecent` still appears under Apple's deprecated constants, with the note "Use mostRecent instead." Apple's documentation data lists it from iOS 12.0 and deprecated at iOS 13.0.

Combining is restricted: "You cannot combine a discrete option with a cumulative option. You can, however, combine multiple discrete options together to perform multiple calculations. You can also combine the separateBySource option with any of the other options."

## Cumulative or discrete is a property of the type

Which option is valid depends on the quantity type, not on what you want to show. [`stepCount`](/healthkit/activity#id-stepcount) is cumulative, so it takes `cumulativeSum`; [`heartRate`](/healthkit/vital-signs#id-heartrate) is discrete, so it takes the discrete options. Apple's own pointer is that its Data types documentation says whether each type is discrete or cumulative. Our group pages show that aggregation style per identifier, and the [sum-or-average checker](/tools/aggregation-checker) looks it up for a single type and covers what happens when the wrong one is chosen.

## A minimal read

```swift
import HealthKit

func readStepTotal(store: HKHealthStore, start: Date, end: Date) {
    let stepType = HKQuantityType(.stepCount)
    let window = HKQuery.predicateForSamples(withStart: start, end: end, options: [])

    let query = HKStatisticsQuery(quantityType: stepType,
                                  quantitySamplePredicate: window,
                                  options: .cumulativeSum) { _, statistics, error in
        guard let statistics else { return }      // nil only when error is set
        let steps = statistics.sumQuantity()?.doubleValue(for: .count())
        DispatchQueue.main.async {
            // show steps; nil means no sum was produced
        }
    }
    store.execute(query)
}
```

The handler runs on a background queue; Apple's discussion says the results handler "is executed on the same background queue (but not necessarily on the same thread)" and that you typically dispatch to the main queue to update the interface.

## Per-source numbers

By default, Apple says, "these queries automatically merge the data from all of your data sources before performing the calculations. If you want to merge the data yourself, you can set the separateBySource option. You can then request the statistical data for each source separately." The per-source accessors take an `HKSource`, such as `sumQuantity(for:)`, and `sources` lists "all the sources contributing to these statistics."

Apple's page does not describe the merge rule itself. If you need to explain to a user why your total differs from a per-device breakdown, [deduplicating health data](/architecture/deduplicate-health-data) covers what overlapping sources do to totals.

## Pitfalls

- **Wrong accessor, silent nil.** Ask for `cumulativeSum` and read `averageQuantity()` and you get `nil` with no error. Match the accessor to the option every time.
- **Discrete plus cumulative.** Apple says the two kinds of option cannot be combined. If a screen needs a sum and an average, run two queries.
- **Workouts and correlations are not supported.** Apple's instruction is to query those yourself and compute the figures in your own code.
- **Boundary samples.** Apple's class page does not say how a sample that straddles the edge of the predicate's window contributes to a sum. How the window itself matches samples is covered under [query predicates](/healthkit-queries/healthkit-query-predicates); for a figure that has to agree with the Health app, test a window boundary on a device rather than assume.
- **Large ranges are slow.** Apple warns that statistical calculations can take considerable time over many samples. Narrow the predicate rather than computing over all history.
- **nil statistics.** The handler's statistics object is documented as nil if an error occurs. Apple does not document what a query with no matching samples returns, so handle both a nil object and a nil quantity.

## FAQ

### Why does sumQuantity() return nil on my HKStatisticsQuery result?

Most often because the query was not created with the cumulativeSum option. Apple's documentation for sumQuantity() says it returns the sum only if cumulativeSum was set and returns nil otherwise, and the HKStatistics page says every accessor must match the option used to create the query. If you did set cumulativeSum, check the error parameter, since the statistics object itself is nil when an error occurs, and remember a discrete type such as heart rate cannot take a cumulative option at all.

[Permalink](https://aifitnessapi.com/healthkit-queries/hkstatisticsquery#faq-1)

### Can one HKStatisticsQuery return both a sum and an average?

No. Apple documents that you cannot combine a discrete option with a cumulative option in HKStatisticsOptions. You can combine several discrete options, such as discreteAverage, discreteMin and discreteMax, in one query, and you can add separateBySource to either kind. A screen that needs both a total and an average of different quantities runs one query per type.

[Permalink](https://aifitnessapi.com/healthkit-queries/hkstatisticsquery#faq-2)

### What does the separateBySource option do in an HKStatisticsQuery?

It makes HealthKit calculate the requested statistics separately for each source as well. Apple explains that statistics queries merge data from all sources by default, and that with separateBySource you can request the data for each source separately, using accessors that take an HKSource such as sumQuantity(for:) or averageQuantity(for:). The statistics object's sources property lists every source that contributed.

[Permalink](https://aifitnessapi.com/healthkit-queries/hkstatisticsquery#faq-3)

### Can I use HKStatisticsQuery to total workout distance or energy?

Not over HKWorkout objects directly. Apple states that statistics queries work with quantity samples only, and that to calculate statistics over workouts or correlation samples you must perform the appropriate query and process the data yourself. Run a sample query for the workouts you want and compute the figure in your own code, or run the statistics query over the underlying quantity type for the time window instead.

[Permalink](https://aifitnessapi.com/healthkit-queries/hkstatisticsquery#faq-4)
