---
title: "HealthKit query predicates: predicateForSamples, HKQueryOptions and HKSamplePredicate"
canonical: "https://aifitnessapi.com/healthkit-queries/healthkit-query-predicates"
cluster: "HealthKit Queries"
primary_query: "HKQuery.predicateForSamples(withStart:end:options:)"
last_reviewed: "2026-10-03"
description: "What HKQuery.predicateForSamples(withStart:end:options:) matches by default, how strictStartDate and strictEndDate change it, and the other predicates."
publisher: "AIFitnessAPI — independent, not sponsored"
cite_as: "\"HealthKit query predicates: predicateForSamples, HKQueryOptions and HKSamplePredicate\", AIFitnessAPI, https://aifitnessapi.com/healthkit-queries/healthkit-query-predicates"
---

# HealthKit query predicates: predicateForSamples, HKQueryOptions and HKSamplePredicate

> HKQuery.predicateForSamples(withStart:end:options:) builds the date-window NSPredicate most HealthKit queries use, and its options decide what counts as inside the window. With no options it matches any sample that overlaps the window: Apple's equivalent format string requires the sample's end date to be at or after the window start and its start date to be before the window end, so a sample that began before midnight still matches today. HKQueryOptions.strictStartDate requires the sample's start to fall inside the window and strictEndDate requires its end to. Because HealthKit converts predicates to SQL, only its own key paths and factory methods work, and the async query descriptors wrap a type and a predicate together as HKSamplePredicate.

- Canonical: https://aifitnessapi.com/healthkit-queries/healthkit-query-predicates
- Last reviewed: 2026-10-03
- Publisher: AIFitnessAPI (https://aifitnessapi.com) — independent, not sponsored
- Cite as: "HealthKit query predicates: predicateForSamples, HKQueryOptions and HKSamplePredicate", AIFitnessAPI, https://aifitnessapi.com/healthkit-queries/healthkit-query-predicates

---

Almost every HealthKit query takes a predicate, and Apple's own articles build theirs with `predicateForSamples(withStart:end:options:)` and an empty options set. That is fine, as long as you know what an empty set means. This page covers the date predicate, the two options, and the other factory methods on `HKQuery`.

## Why HealthKit restricts predicates

Apple's `HKQuery` page explains the constraint: "When HealthKit runs a query, it converts the predicate to SQL and executes the SQL on the underlying store." Two consequences follow, in Apple's words:

- "Predicates improve the performance of your query, both in terms of speed and memory usage. Because the store executes the predicate, it restricts the number of HealthKit objects that it instantiates and returns."
- "Since the store executes these predicates, it limits the type of predicates that you can use. Specifically, HealthKit provides several predicate key paths (for example, HKPredicateKeyPathUUID and HKPredicateKeyPathMetadata). You can create predicates using only these key paths."

So filter in the predicate wherever you can, but only with HealthKit's key paths, which Apple lists under "Predicate format strings" (start and end date, UUID, metadata, quantity, category value, source, device, workout and others), or with the factory methods below.

## The date window and what an empty options set means

The declaration is `class func predicateForSamples(withStart startDate: Date?, end endDate: Date?, options: HKQueryOptions = []) -> NSPredicate`. Apple: "A predicate for samples whose start and end dates fall within the specified time interval. This predicate works only with samples."

That summary sounds stricter than the default really is. Apple's page shows the convenience method beside a predicate format string it calls equivalent, and the format string makes the rule visible:

```swift
let timeInterval =
    HKQuery.predicateForSamplesWithStartDate(myStartDate,
                                             endDate: myEndDate, options: .None)

let explicitTimeInterval = NSPredicate(format: "%K >= %@ AND %K < %@",
                                       HKPredicateKeyPathEndDate, myStartDate,
                                       HKPredicateKeyPathStartDate, myEndDate)
```

That is Apple's sample as published, in an older Swift spelling. Read the format string: the sample's end date must be at or after the window's start, and the sample's start date must be before the window's end. In other words, with no options the predicate matches every sample that overlaps the window, including one that started yesterday evening and ended after midnight.

## strictStartDate and strictEndDate

`HKQueryOptions` is "Constants that describe how a sample's time period overlaps with the target time period." It has two:

| Option | Apple's rule |
| --- | --- |
| `strictStartDate` | "The sample's start time must be equal to or later than the target's start time, and the sample's start time must also be earlier than the target's end time." |
| `strictEndDate` | "The sample's end time must be equal to or later than the target's start time, and the sample's end time must also be earlier than the target's end time." |

Apple documents each option on its own page and does not separately describe passing both.

Which to use depends on what you are building. If you are fetching samples into day buckets, overlap matching means a sample spanning midnight lands in both days' results, so pick one rule, such as `strictStartDate`, and use it everywhere. The bucket boundaries themselves are a separate design question, covered in [timezones and day boundaries](/architecture/timezones-and-day-boundaries).

## The other predicate factories

`HKQuery` offers many more class methods. The ones most apps reach for:

| Method | Apple's description |
| --- | --- |
| `predicateForObjects(from:)` with an `HKSource` | "Returns a predicate that matches all the objects that were created by the provided source." |
| `predicateForObjects(from:)` with a set of `HKDevice` | "Returns a predicate that matches all the objects that were created by any of the provided devices." |
| `predicateForObjects(from:)` with an `HKWorkout` | "Returns a predicate that matches any objects that have been associated with the provided workout." |
| `predicateForObjects(with:)` | "Returns a predicate that matches the objects with the specified universally unique identifiers (UUIDs)." |
| `predicateForObjects(withMetadataKey:operatorType:value:)` | Matches on a metadata value; Apple says the values "must be NSString, NSNumber, or NSDate instances." |
| `predicateForQuantitySamples(with:quantity:)` | "Returns a predicate that matches samples based on the target quantity." Quantity samples only. |
| `predicateForCategorySamples(with:value:)` | "Returns a predicate that checks a category sample's value." Category samples only. |
| `predicateForWorkouts(with:)` | "Returns a predicate for matching workouts based on the type of activity." |
| `predicateForObjectsWithNoCorrelation()` | "Returns a predicate that matches all objects that are not associated with a HealthKit correlation." |

To find the `HKSource` values in the first row, HealthKit has `HKSourceQuery`, "A query that returns a list of sources, such as apps and devices, that have saved matching queries to the HealthKit store". Activity summaries use their own date-component predicates, covered on the [HKActivitySummaryQuery page](/healthkit-queries/hkactivitysummaryquery). For `predicateForCategorySamples(with:value:)`, the value is an integer whose meaning comes from the category type's value enum; [the HealthKit category values reference](/healthkit-category-values) lists that enum for every category type, or says where Apple names none.

## Combining predicates

To apply a date window and a source filter together, combine them with Foundation's `NSCompoundPredicate`, whose `init(andPredicateWithSubpredicates:)` "Returns a new predicate that you form using an AND operation on the predicates in a specified array."

```swift
import HealthKit

func todaysStepsFromOneApp(store: HKHealthStore, source: HKSource, start: Date, end: Date) {
    let window = HKQuery.predicateForSamples(withStart: start, end: end, options: .strictStartDate)
    let fromSource = HKQuery.predicateForObjects(from: source)
    let both = NSCompoundPredicate(andPredicateWithSubpredicates: [window, fromSource])

    let query = HKSampleQuery(sampleType: HKQuantityType(.stepCount),
                              predicate: both,
                              limit: HKObjectQueryNoLimit,
                              sortDescriptors: nil) { _, results, error in
        // results: today's step samples written by that source
    }
    store.execute(query)
}
```

## HKSamplePredicate for the async API

The Swift concurrency descriptors take `HKSamplePredicate` instead of `NSPredicate`. Apple: it "bundles an HKSampleType and an optional NSPredicate", and because it is generic, "any query that you build using this structure returns properly typed results." Factory methods include `quantitySample(type:predicate:)`, `categorySample(type:predicate:)`, `correlation(type:predicate:)`, `workout(_:)`, `workoutRoute(_:)`, `heartbeatSeries(_:)`, `electrocardiogram(_:)`, `audiogram(_:)` and the general `sample(type:predicate:)`. The `NSPredicate` you put inside is built exactly as above. It is listed from iOS 15.4 and watchOS 8.5; see the [async query descriptors page](/healthkit-queries/healthkit-async-query-descriptors).

## Pitfalls

- **Reading the summary sentence literally.** Apple's one-line description says samples whose dates "fall within" the interval, but its own equivalent format string matches overlap. Trust the format string.
- **Double counting across adjacent windows.** With no options, a sample that crosses the boundary between two sample queries' windows appears in both. Choose a strict option when fetching samples into buckets.
- **Arbitrary format strings.** Apple says you can create predicates using only HealthKit's key paths, because the store runs them as SQL. Apple does not document what happens with an unsupported key path; do not find out in production.
- **Filtering in memory instead.** Apple notes predicates cut speed and memory costs because the store evaluates them. Fetching everything and filtering afterwards gives that up.
- **Metadata values of the wrong type.** For metadata predicates, Apple says target values must be `NSString`, `NSNumber` or `NSDate` instances.
- **Statistics queries and boundaries.** These predicates also feed [HKStatisticsQuery](/healthkit-queries/hkstatisticsquery). Apple's statistics pages do not say how a sample straddling the window edge contributes to a sum, so test boundary cases on a device.

## FAQ

### Does predicateForSamples(withStart:end:options:) match samples that start before the window?

Yes, when no options are passed. Apple's documentation shows the convenience method alongside an equivalent predicate format string that requires the sample's end date to be at or after the window start and its start date to be before the window end. Any sample overlapping the window therefore matches, including one that began before the window opened. Pass strictStartDate if a sample should count only when it starts inside the window.

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

### What is the difference between strictStartDate and strictEndDate in HKQueryOptions?

They choose which end of the sample must fall inside the window. Apple documents that with strictStartDate the sample's start time must be equal to or later than the window's start and earlier than its end, and with strictEndDate the same test applies to the sample's end time. Without either option, a sample matches as long as it overlaps the window at all.

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

### Can I filter a HealthKit query with any NSPredicate format string?

No. Apple explains that HealthKit converts a query's predicate to SQL and runs it on the underlying store, which limits the predicates you can use: you can create them only with HealthKit's own predicate key paths, such as HKPredicateKeyPathStartDate, HKPredicateKeyPathUUID or HKPredicateKeyPathMetadata, or with the factory methods on HKQuery. In exchange, the filtering happens in the store, which Apple says improves speed and memory use.

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

### How do I limit a HealthKit query to data written by one app or device?

Use HKQuery.predicateForObjects(from:). Apple provides versions that take a single HKSource, a set of sources, a set of HKDevice values or a set of source revisions, each returning a predicate that matches objects created by them. HKSourceQuery returns the list of sources that have saved matching data, and NSCompoundPredicate can combine the source predicate with a date window.

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

### What is HKSamplePredicate used for in HealthKit?

It is the filter type for HealthKit's Swift concurrency query descriptors. Apple describes it as bundling an HKSampleType with an optional NSPredicate, and because it is generic, queries built from it return properly typed results such as HKQuantitySample instead of the base HKSample class. You create one with factory methods such as quantitySample(type:predicate:) or workout(_:), and it is listed from iOS 15.4 and watchOS 8.5.

[Permalink](https://aifitnessapi.com/healthkit-queries/healthkit-query-predicates#faq-5)
