Documentation Index
Fetch the complete documentation index at: https://mintlify.com/kingstinct/react-native-healthkit/llms.txt
Use this file to discover all available pages before exploring further.
HealthKit anchors enable efficient data synchronization by tracking which samples you’ve already fetched. Instead of querying all data repeatedly, anchors let you fetch only what’s new or changed since your last query.
What are anchors?
An anchor is a base64-encoded string returned by HealthKit that represents a sync point. After each successful query, HealthKit provides a new anchor representing the current state. Use this anchor in your next query to receive only samples added or deleted since then.
Query with anchors
Use anchor-based query functions to get incremental updates:
import { queryQuantitySamplesWithAnchor } from '@kingstinct/react-native-healthkit';
// Initial query without anchor
const { newAnchor, samples, deletedSamples } = await queryQuantitySamplesWithAnchor(
'HKQuantityTypeIdentifierStepCount',
{
limit: 0 // 0 means no limit
}
);
console.log('Samples:', samples.length);
console.log('Deleted:', deletedSamples.length);
console.log('Anchor:', newAnchor);
// Store the anchor for next time
await AsyncStorage.setItem('stepCountAnchor', newAnchor);
Incremental sync
On subsequent queries, use the stored anchor to get only new changes:
// Retrieve stored anchor
const storedAnchor = await AsyncStorage.getItem('stepCountAnchor');
// Query for changes since last sync
const { newAnchor, samples, deletedSamples } = await queryQuantitySamplesWithAnchor(
'HKQuantityTypeIdentifierStepCount',
{
limit: 0,
anchor: storedAnchor
}
);
console.log('New samples:', samples.length);
console.log('Deleted samples:', deletedSamples.length);
// Update stored anchor
await AsyncStorage.setItem('stepCountAnchor', newAnchor);
Anchor queries return both new samples and deleted samples. Handle deletions to keep your local data synchronized with HealthKit.
Limit parameter
Control how many samples to fetch per sync:
// Fetch in batches of 100
const result = await queryQuantitySamplesWithAnchor(
'HKQuantityTypeIdentifierStepCount',
{
limit: 100,
anchor: storedAnchor
}
);
// Continue fetching until no more samples
while (result.samples.length === 100) {
const nextResult = await queryQuantitySamplesWithAnchor(
'HKQuantityTypeIdentifierStepCount',
{
limit: 100,
anchor: result.newAnchor
}
);
// Process nextResult...
}
Deleted samples
The deletedSamples array contains UUIDs of removed data:
const { samples, deletedSamples, newAnchor } = await queryQuantitySamplesWithAnchor(
'HKQuantityTypeIdentifierStepCount',
{
limit: 0,
anchor: storedAnchor
}
);
// Handle deletions
for (const deleted of deletedSamples) {
console.log('Deleted sample UUID:', deleted.uuid);
// Remove from your local database
await database.delete('samples', { uuid: deleted.uuid });
}
// Handle additions
for (const sample of samples) {
console.log('New sample:', sample);
// Add to your local database
await database.insert('samples', sample);
}
Category samples with anchors
Anchor queries work with all sample types:
import { queryCategorySamplesWithAnchor } from '@kingstinct/react-native-healthkit';
const { newAnchor, samples, deletedSamples } = await queryCategorySamplesWithAnchor(
'HKCategoryTypeIdentifierSleepAnalysis',
{
limit: 0,
anchor: storedAnchor
}
);
Workout samples with anchors
Sync workout data efficiently:
import { queryWorkoutSamplesWithAnchor } from '@kingstinct/react-native-healthkit';
const { workouts, deletedSamples, newAnchor } = await queryWorkoutSamplesWithAnchor({
limit: 0,
anchor: storedAnchor
});
console.log('New workouts:', workouts.length);
console.log('Deleted workouts:', deletedSamples.length);
Note that workout anchor queries return workouts instead of samples in the response object.
Correlation samples with anchors
Sync correlation data like blood pressure:
import { queryCorrelationSamplesWithAnchor } from '@kingstinct/react-native-healthkit';
const { correlations, deletedSamples, newAnchor } = await queryCorrelationSamplesWithAnchor(
'HKCorrelationTypeIdentifierBloodPressure',
{
limit: 0,
anchor: storedAnchor
}
);
Best practices for syncing
Store anchors persistently
Save anchors to persistent storage (AsyncStorage, database, etc.) so you can resume syncing after app restarts.import AsyncStorage from '@react-native-async-storage/async-storage';
const ANCHOR_KEY = 'healthkit_anchor_steps';
// Save anchor
await AsyncStorage.setItem(ANCHOR_KEY, newAnchor);
// Load anchor
const anchor = await AsyncStorage.getItem(ANCHOR_KEY);
Use separate anchors per data type
Each data type should have its own anchor for independent syncing.const anchors = {
steps: await AsyncStorage.getItem('anchor_steps'),
heartRate: await AsyncStorage.getItem('anchor_heartRate'),
sleep: await AsyncStorage.getItem('anchor_sleep'),
};
Handle errors gracefully
If a sync fails, keep the old anchor to retry from the same point.try {
const result = await queryQuantitySamplesWithAnchor(
'HKQuantityTypeIdentifierStepCount',
{ limit: 0, anchor: oldAnchor }
);
// Process data...
// Only save anchor after successful processing
await AsyncStorage.setItem('anchor_steps', result.newAnchor);
} catch (error) {
console.error('Sync failed, will retry:', error);
// Keep old anchor for retry
}
Implement periodic syncing
Regularly sync data in the background or when the app opens.useEffect(() => {
const syncData = async () => {
const anchor = await AsyncStorage.getItem('anchor_steps');
const result = await queryQuantitySamplesWithAnchor(
'HKQuantityTypeIdentifierStepCount',
{ limit: 0, anchor }
);
// Process result...
await AsyncStorage.setItem('anchor_steps', result.newAnchor);
};
syncData();
// Sync every hour
const interval = setInterval(syncData, 60 * 60 * 1000);
return () => clearInterval(interval);
}, []);
Combine with filters
Anchors work with all query filters:
const { samples, deletedSamples, newAnchor } = await queryQuantitySamplesWithAnchor(
'HKQuantityTypeIdentifierStepCount',
{
limit: 0,
anchor: storedAnchor,
filter: {
date: {
startDate: new Date(2024, 0, 1)
}
}
}
);
Initial vs incremental sync
Pattern for handling first sync vs updates:
async function syncSteps() {
const anchor = await AsyncStorage.getItem('anchor_steps');
const result = await queryQuantitySamplesWithAnchor(
'HKQuantityTypeIdentifierStepCount',
{
limit: 0,
anchor: anchor ?? undefined // undefined for initial sync
}
);
if (!anchor) {
// Initial sync - full data load
console.log('Initial sync:', result.samples.length, 'samples');
await initializeDatabase(result.samples);
} else {
// Incremental sync - only changes
console.log('Incremental sync:', result.samples.length, 'new,',
result.deletedSamples.length, 'deleted');
await updateDatabase(result.samples, result.deletedSamples);
}
await AsyncStorage.setItem('anchor_steps', result.newAnchor);
}
Never reuse an anchor with a different data type. Each identifier requires its own anchor. Using the wrong anchor will return incorrect results.
Anchors dramatically improve sync performance:
// Without anchors - queries ALL samples every time
const allSamples = await queryQuantitySamples('HKQuantityTypeIdentifierStepCount', {
limit: 0
});
console.log('Fetched:', allSamples.length); // Could be 100,000+ samples
// With anchors - queries only CHANGES since last sync
const { samples } = await queryQuantitySamplesWithAnchor(
'HKQuantityTypeIdentifierStepCount',
{ limit: 0, anchor: storedAnchor }
);
console.log('Fetched:', samples.length); // Typically 0-100 samples
For apps that sync health data to a server, anchors are essential. They minimize data transfer, reduce battery usage, and ensure you never miss changes or deletions.