← Todos los articulos

HealthKit en iOS 27: zonas de entrenamiento y nuevos tipos

Durante años, HealthKit ha tratado las zonas de frecuencia cardíaca de entrenamiento como un ciudadano no oficial: cada app de carrera y ciclismo que dibujaba un gráfico de barras de cinco zonas calculaba esas zonas por su cuenta, extraía las lecturas crudas de frecuencia cardíaca de HKQuantitySample, las agrupaba en bandas contra umbrales fijados en el código o introducidos por el usuario, y sumaba a mano el tiempo de cada banda. iOS 27 pone fin a ese trabajo artesanal. Las zonas pasan a ser un tipo estructurado de HealthKit que el sistema genera, que la persona puede editar en la Configuración de Salud y que tu app lee de vuelta con el tiempo en cada zona ya contabilizado. La misma versión añade dos tipos de categoría de salud reproductiva, menopausalState y bleedingAfterMenopause, que cierran una brecha en el modelo de seguimiento del ciclo de HealthKit.1

Dos hilos recorren las novedades de iOS 27, y comparten una misma forma. El hilo de las zonas de entrenamiento toma un cálculo que antes pertenecía a las apps y lo lleva al framework, donde la definición de zonas de una persona se mantiene consistente en cada app que la consulta. El hilo de los nuevos tipos le da a etapas de la vida que HealthKit no podía representar antes una muestra de categoría con tipo propio. Ambos siguen la gramática existente de HealthKit: tipos de cantidad para valores medidos, tipos de categoría para eventos y estados. Este artículo recorre cada uno frente a la documentación de Apple, con el enfoque habitual del clúster: qué añade una app que ya incorpora HealthKit para obtener cada capacidad.

TL;DR

  • HKWorkoutZoneConfiguration define un conjunto completo de zonas para un tipo de cantidad. El sistema genera las zonas automáticamente a partir de las métricas de salud de la persona, esta puede editarlas en la Configuración de Salud, y una app puede aportar zonas personalizadas para un entrenamiento específico.2
  • HKWorkoutZoneGroup empareja una configuración de zonas con sus datos de tiempo en zona. Lo lees de vuelta desde las instancias de entrenamientos completados para obtener las duraciones por zona que el framework ya calculó, en lugar de agrupar tú mismo las muestras de frecuencia cardíaca.3
  • preferredWorkoutZoneConfiguration(for:) en HKHealthStore devuelve la configuración de zonas preferida de la persona para un tipo de cantidad, de modo que tus gráficos coincidan con los umbrales que esa persona ve en todas partes.4
  • menopausalState, junto con la enumeración HKCategoryValueMenopausalState, registra el estado menopáusico en un punto temporal concreto; la fecha de inicio y la fecha de fin de la muestra deben ser idénticas o el guardado falla.56
  • bleedingAfterMenopause registra el sangrado posmenopáusico como un intervalo con un valor de intensidad, manteniéndolo clínicamente diferenciado del flujo menstrual.7

Las zonas de entrenamiento siempre fueron tuyas para calcularlas

Antes de iOS 27, una app de entrenamiento que quería zonas de frecuencia cardíaca hacía el trabajo completo. Consultaba los datos de HKQuantitySample de frecuencia cardíaca para el rango temporal del entrenamiento, decidía dónde quedaban los límites de las zonas (220 menos la edad, porcentajes del umbral de lactato, o un valor que el usuario escribía en una pantalla de configuración), recorría las muestras en orden y acumulaba los segundos pasados en cada banda. Cada app elegía su propia aritmética de límites, así que una persona que corría con dos apps veía dos “Zona 3” distintas. Ninguno de los datos de zona vivía en HealthKit, así que nada de ello se sincronizaba ni resultaba portable.

iOS 27 introduce HKWorkoutZoneConfiguration, una estructura que define un conjunto completo de zonas para un tipo de cantidad.2 La configuración lleva un arreglo ordenado de zonas e identifica el tipo de cantidad al que aplica. El cambio que importa es de dónde provienen las zonas. La documentación de Apple indica que el sistema las genera automáticamente a partir de las métricas de salud de la persona; que la persona puede configurar las zonas manualmente en la Configuración de Salud; o que una app puede aportar zonas personalizadas para entrenamientos específicos.2 Las definiciones de zona dejan de ser un detalle privado de cada app y pasan a ser un estado compartido del que el framework es dueño.

import HealthKit

let heartRateType = HKQuantityType(.heartRate)

// A custom configuration an app supplies for one workout.
// makeIntervalZones is your own builder; construct the configuration
// from the ordered zones your workout uses, per HKWorkoutZoneConfiguration's
// declaration. The configuration identifies the quantity type the zones apply to.
let configuration: HKWorkoutZoneConfiguration = makeIntervalZones(for: heartRateType)

La estructura complementaria es HKWorkoutZoneGroup, que contiene la configuración de zonas y los datos de tiempo en zona para un tipo de cantidad.3 Un grupo de zonas combina un HKWorkoutZoneConfiguration con los datos de tiempo por zona, de modo que leer un grupo te entrega tanto los límites como cuánto tiempo pasó la persona dentro de cada uno. La documentación de Apple describe el acceso a los grupos de zonas desde las instancias de entrenamientos completados, para recuperar los datos de zona de entrenamientos finalizados, y desde una fuente de entrenamiento en vivo, para obtener información de zona en tiempo real durante una sesión activa.3 El framework hace la agrupación en bandas. Tu código lee el resultado.

Watch on Apple Developer ↗
Apple muestra los datos de zona fluyendo desde la Configuración de Salud de una persona hacia la app, con el tiempo pasado en cada zona calculado por HealthKit a partir de las muestras entrantes.

En la sesión 207, Apple afirma que, con las zonas de entrenamiento integradas en HealthKit, el tiempo en cada zona se calcula automáticamente a partir de las muestras entrantes durante el entrenamiento, de modo que la app lee las duraciones ya sumadas desde zoneGroupsByType en lugar de agrupar ella misma las muestras de frecuencia cardíaca.8

import HealthKit

func summarize(_ group: HKWorkoutZoneGroup) {
    // The group bundles the configuration (the zone boundaries)
    // with the time-in-zone data the framework already computed.
    let configuration = group.configuration

    for zone in group.zones {
        // Render each zone's accumulated time. No sample-walking,
        // no manual bucketing — the durations arrive pre-summed.
        render(zone)
    }
}

La tercera pieza vincula la configuración a la persona en lugar de a la app. preferredWorkoutZoneConfiguration(for:) es un método de instancia en HKHealthStore que devuelve la configuración de zonas preferida de alguien para el tipo de cantidad especificado:4

func preferredWorkoutZoneConfiguration(
    for quantityType: HKQuantityType
) async throws -> HKWorkoutZoneConfiguration?

La documentación de Apple detalla el contrato de retorno: el método devuelve las zonas configuradas manualmente por la persona en la Configuración de Salud, o las zonas generadas por el sistema si la persona no ha establecido valores personalizados, o nil si no ha configurado zonas para ese tipo de cantidad en absoluto.4 Las zonas generadas por el sistema se actualizan periódicamente a medida que cambian las métricas de salud de la persona; las zonas que la persona estableció a mano permanecen constantes hasta que vuelve a editarlas. La intención documentada es que las apps muestren información de zona que se alinee con las preferencias de la persona en cada entrenamiento.4 Recurre al método cuando quieras que tu Zona 3 coincida con la Zona 3 que la persona ve en todos los demás lugares, en vez de inventar la tuya propia.

import HealthKit

let store = HKHealthStore()
let heartRateType = HKQuantityType(.heartRate)

func loadPreferredZones() async throws -> HKWorkoutZoneConfiguration? {
    // nil means the person has not configured zones for heart rate.
    // Fall back to your own defaults only in that case.
    try await store.preferredWorkoutZoneConfiguration(for: heartRateType)
}

Vale la pena nombrar la asimetría. preferredWorkoutZoneConfiguration(for:) lee la preferencia estable de la persona, lo que dibuja gráficos consistentes antes, durante y después de un entrenamiento. Un HKWorkoutZoneConfiguration personalizado que construyes tú mismo cubre el caso en que un entrenamiento específico necesita zonas distintas de las predeterminadas de la persona, como una sesión estructurada de intervalos con sus propias bandas. La mayoría de las apps querrán la configuración preferida; la vía personalizada es para cuando la estructura de un entrenamiento exige sus propias zonas.

Nuevos tipos de categoría: menopausalState y bleedingAfterMenopause

El modelo de seguimiento del ciclo de HealthKit manejaba la menstruación, pero no tenía una forma con tipo de registrar en qué punto de la transición menopáusica se encontraba una persona, ni de registrar el sangrado que ocurre después de que la menstruación ha terminado. iOS 27 añade ambos como tipos de categoría, siguiendo el mismo patrón de HKCategorySample que el artículo sobre HealthKit en iOS 26 cubrió para las sesiones de mindfulness y el sueño.

menopausalState es un identificador de tipo de categoría para muestras que registran el estado menopáusico de una persona.5 Cada muestra es una entrada en un punto temporal concreto, y la documentación de Apple impone una restricción estricta: la fecha de inicio debe ser igual a la fecha de fin, y guardar una muestra en la que ambas difieren produce un error.5 El valor de cada muestra es un caso de la enumeración HKCategoryValueMenopausalState, que indica el estado menopáusico en un punto temporal registrado.6 Dado que la lista de casos del resumen técnico se omite en la exposición de Apple, trata la enumeración como la fuente de verdad para los valores válidos en lugar de adivinar los nombres de los casos; el comportamiento documentado es que cada muestra almacena uno de esos casos en una fecha específica.6

import HealthKit

let store = HKHealthStore()
let menopausalType = HKCategoryType(.menopausalState)

func record(state: HKCategoryValueMenopausalState, on date: Date) async throws {
    // Point-in-time sample: start and end MUST be identical.
    // A mismatched start/end is a save error, by documented design.
    let sample = HKCategorySample(
        type: menopausalType,
        value: state.rawValue,
        start: date,
        end: date
    )
    try await store.save(sample)
}

El modelo de punto temporal define cómo interpretas los datos. La documentación de Apple señala que las apps pueden leer múltiples muestras a lo largo del tiempo para derivar interpretaciones de nivel superior: períodos activos, transiciones o un estado actual.6 Una sola muestra afirma que un estado determinado aplicó en una fecha determinada, y el framework deja en manos de la app la tarea de hilvanar las muestras en una línea temporal. Apple plantea cada entrada como un cambio de estado, una confirmación de que un estado aplicó en esa fecha, o ambas cosas, según cómo la app lea la serie.5

bleedingAfterMenopause es un identificador de tipo de categoría para muestras que registran el sangrado después de la menopausia.7 La distinción clínica es la razón de que exista como un tipo propio. Tras la menopausia, la menstruación ha terminado, de modo que el sangrado posmenopáusico no es flujo menstrual ni sangrado intermenstrual; la documentación de Apple lo califica de clínicamente diferenciado de ambos.7 A diferencia de la muestra de estado menopáusico de punto temporal, una muestra de bleedingAfterMenopause representa un intervalo de sangrado y almacena un valor de intensidad.7

import HealthKit

let store = HKHealthStore()
let bleedingType = HKCategoryType(.bleedingAfterMenopause)

// `intensityValue` is the raw value of the intensity enum Apple defines for
// the bleedingAfterMenopause type; confirm the concrete case set in its declaration.
func record(intensityValue: Int, from start: Date, to end: Date) async throws {
    // An interval sample, not point-in-time: start and end differ,
    // and the value carries the bleeding intensity.
    let sample = HKCategorySample(
        type: bleedingType,
        value: intensityValue,
        start: start,
        end: end
    )
    try await store.save(sample)
}

La separación entre ambos tipos refleja la que el artículo de iOS 26 trazó entre las muestras de categoría de punto temporal y las de intervalo. menopausalState colapsa su ventana a un único instante y porta un estado. bleedingAfterMenopause conserva una ventana real [start, end] y porta una intensidad. Elegir la forma equivocada para cualquiera de los dos es un error de guardado o un registro sin sentido, así que el tipo que escojas codifica la naturaleza del dato antes de que escribas una línea de código de consulta.

Ruta de adopción

Una app de entrenamiento o de salud que ya incorpora HealthKit añade la superficie de iOS 27 de forma incremental; nada de esto reescribe la maquinaria de autorización ni de guardado de muestras que cubrió el artículo sobre los patrones de iOS 26.

  1. Elimina tu aritmética de zonas. Cualquier app que calcule zonas de frecuencia cardíaca a mano debería llamar a preferredWorkoutZoneConfiguration(for:) para el tipo de cantidad correspondiente y renderizar la configuración devuelta, recurriendo a tus propios valores predeterminados solo cuando la llamada devuelva nil.4 Tus zonas ahora coinciden con lo que la persona ve en todo el sistema.
  2. Lee HKWorkoutZoneGroup en lugar de agrupar muestras. Para los resúmenes de entrenamientos completados, extrae el grupo de zonas y renderiza sus datos de tiempo en zona ya calculados, en vez de recorrer las muestras crudas de frecuencia cardíaca y acumular tú mismo las duraciones.3
  3. Aporta un HKWorkoutZoneConfiguration personalizado solo cuando un entrenamiento necesite sus propias bandas. Las sesiones estructuradas de intervalos con zonas a medida son el caso para una configuración personalizada; todo lo demás usa la configuración preferida de la persona.2
  4. Añade los nuevos tipos de categoría a tu solicitud de autorización. Una app de seguimiento del ciclo añade menopausalState y bleedingAfterMenopause a sus conjuntos de escritura y lectura, y luego registra menopausalState como muestras de punto temporal (inicio igual a fin) y bleedingAfterMenopause como muestras de intervalo con una intensidad.57
  5. Audita tus fechas de inicio y fin por tipo. El guardado del estado menopáusico falla si el inicio y el fin difieren; la muestra de sangrado es un intervalo y espera que difieran. Acierta con la forma de la ventana antes de enviar la ruta de guardado.57

Preguntas frecuentes

¿Todavía tengo que calcular yo mismo las zonas de frecuencia cardíaca en iOS 27?

No, no en el caso común. preferredWorkoutZoneConfiguration(for:) devuelve la configuración de zonas preferida de la persona para un tipo de cantidad (sus zonas configuradas manualmente en la Configuración de Salud, o las zonas generadas por el sistema, o nil si no ha configurado ninguna), y HKWorkoutZoneGroup porta los datos de tiempo en zona que el framework ya calculó para un entrenamiento.34 Calculas las zonas por tu cuenta solo cuando un entrenamiento específico necesita bandas personalizadas que difieren de las predeterminadas de la persona, en cuyo caso construyes un HKWorkoutZoneConfiguration para ese entrenamiento.2

¿Cuál es la diferencia entre HKWorkoutZoneConfiguration y HKWorkoutZoneGroup?

HKWorkoutZoneConfiguration define un conjunto completo de zonas para un tipo de cantidad: las zonas ordenadas y el tipo de cantidad al que aplican.2 HKWorkoutZoneGroup contiene tanto esa configuración como los datos de tiempo en zona para un tipo de cantidad, de modo que un grupo te indica los límites y cuánto tiempo pasó la persona en cada zona.3 Lees los grupos de zonas desde las instancias de entrenamientos completados para las sesiones finalizadas y desde una fuente en vivo para obtener información de zona en tiempo real.3

¿Qué devuelve preferredWorkoutZoneConfiguration(for:) cuando la persona nunca configuró zonas?

Devuelve las zonas generadas por el sistema si la persona no ha establecido valores personalizados, y nil solo si no ha configurado zonas para ese tipo de cantidad en absoluto.4 Cuando la persona estableció zonas manualmente en la Configuración de Salud, el método devuelve esas, y permanecen constantes hasta que la persona las edita; las zonas generadas por el sistema se actualizan periódicamente a medida que cambian sus métricas de salud.4 Ramifica según nil para aportar tus propios valores predeterminados de respaldo.

¿Por qué falla el guardado de una muestra de menopausalState con un error de fecha?

Porque menopausalState registra un estado en un único punto temporal, el framework exige que la fecha de inicio y la de fin de la muestra sean idénticas, y una muestra cuyas fechas difieren produce un error al guardar.5 Establece ambas en la misma Date. Esa restricción la distingue de bleedingAfterMenopause, que representa un intervalo y espera que el inicio y el fin difieran.7

¿En qué se diferencia bleedingAfterMenopause del flujo menstrual?

Tras la menopausia, la menstruación ha terminado, así que el sangrado posmenopáusico es clínicamente diferente del flujo menstrual y del sangrado intermenstrual; Apple le da un tipo de categoría aparte por esa razón.7 Cada muestra de bleedingAfterMenopause almacena un intervalo y un valor de intensidad, y para el seguimiento del estado relacionado usas el tipo menopausalState.7

El clúster completo del Ecosistema Apple acompaña a este artículo: los patrones de autorización y tipos de muestra de HealthKit en iOS 26 sobre los que esto se construye; el ciclo de vida del entrenamiento en watchOS, donde los datos de zona se originan durante una sesión en vivo; el contrato de ejecución de watchOS que rige la ejecución en segundo plano en la muñeca; y las tres superficies de una app de iOS que sitúan a HealthKit como la capa de fuente de datos. El eje central es la serie del Ecosistema Apple. Para el contexto más amplio de iOS con agentes de IA, consulta la guía de desarrollo de agentes en iOS.

Referencias


  1. Documentación para desarrolladores de Apple: HealthKit. La referencia del framework que cubre los tipos de cantidad, los tipos de categoría, las muestras y las novedades de iOS 27 sobre zonas de entrenamiento y salud reproductiva. 

  2. Documentación para desarrolladores de Apple: HKWorkoutZoneConfiguration (iOS 27.0). “A structure that defines a complete set of zones for a quantity type.” Declarado como struct HKWorkoutZoneConfiguration; contiene un arreglo ordenado de zonas, identifica su tipo de cantidad y admite zonas personalizadas generadas por el sistema, configuradas por la persona o aportadas por la app. 

  3. Documentación para desarrolladores de Apple: HKWorkoutZoneGroup (iOS 27.0). “A structure that contains zone configuration and time-in-zone data for a quantity type.” Declarado como struct HKWorkoutZoneGroup; combina una configuración con datos de tiempo en zona, accesible desde las instancias de entrenamientos completados y desde fuentes de entrenamiento en vivo. 

  4. Documentación para desarrolladores de Apple: preferredWorkoutZoneConfiguration(for:) (iOS 27.0). “Returns someone’s preferred zone configuration for the specified quantity type.” Declarado como func preferredWorkoutZoneConfiguration(for quantityType: HKQuantityType) async throws -> HKWorkoutZoneConfiguration?; devuelve zonas configuradas manualmente, zonas generadas por el sistema o nil

  5. Documentación para desarrolladores de Apple: menopausalState (iOS 27.0). “An identifier for samples that record a person’s menopausal state.” Declarado como static let menopausalState: HKCategoryTypeIdentifier; las muestras de punto temporal requieren fechas de inicio y fin idénticas o el guardado falla. 

  6. Documentación para desarrolladores de Apple: HKCategoryValueMenopausalState (iOS 27.0). “A value that indicates the menopausal state at a recorded point in time.” Declarado como enum HKCategoryValueMenopausalState; las apps pueden consultar múltiples muestras a lo largo del tiempo para derivar períodos activos, transiciones o el estado actual. 

  7. Documentación para desarrolladores de Apple: bleedingAfterMenopause (iOS 27.0). “An identifier for samples that record bleeding after menopause.” Declarado como static let bleedingAfterMenopause: HKCategoryTypeIdentifier; clínicamente diferenciado del flujo menstrual, cada muestra es un intervalo con un valor de intensidad. 

  8. Apple, sesión 207 de la WWDC26, “Deliver workout insights with HealthKit workout zones.” developer.apple.com/videos/play/wwdc2026/207. Con las zonas de frecuencia cardíaca y de potencia de ciclismo integradas en HealthKit en iOS 27, el tiempo en cada zona se calcula automáticamente a partir de las muestras entrantes durante el entrenamiento, y una app lo lee de vuelta desde el diccionario zoneGroupsByType en HKWorkout o HKWorkoutActivity

Artículos relacionados

Novedades de SwiftUI para iOS 27

iOS 27 rehace las listas, los documentos, las barras de herramientas y los errores de SwiftUI: arrastrar para reordenar,…

25 min de lectura

SwiftData en iOS 27: Observación e historial

iOS 27 le da a SwiftData observación de cambios de primera clase con ResultsObserver, observación de historial persisten…

12 min de lectura

The Robots Are Taking Exams in My Search Console

First-party GSC data: 91% of 3.8M impressions fail a human-query filter. Exam questions, pasted errors, and agent sweeps…

10 min de lectura