{"$schema": "https://raw.githubusercontent.com/guitaripod/wwdc-sessions/master/schema/transcript.schema.json", "id": "wwdc2026-207", "language": "eng", "source": "https://developer.apple.com/videos/play/wwdc2026/207", "wordCount": 1802, "segments": [{"start": 7.0, "text": "Hello!"}, {"start": 8.0, "text": "My name is Seth and I am an engineer on the HealthKit team."}, {"start": 11.0, "text": "There are many health and fitness apps in the App Store that help people plan,"}, {"start": 15.0, "text": "track, and visualize their fitness goals."}, {"start": 18.0, "text": "When people grant your app permission,"}, {"start": 20.0, "text": "your app can leverage HealthKit's centralized,"}, {"start": 22.0, "text": "secure database and powerful APIs to create workouts"}, {"start": 26.0, "text": "and access the underlying health data."}, {"start": 29.0, "text": "Workout zones can leverage health data,"}, {"start": 31.0, "text": "to help people train smarter by tracking time spent at specific intensity levels."}, {"start": 37.0, "text": "Heart rate zones are a training resource that's personalized to an individual."}, {"start": 42.0, "text": "They are calculated by considering one's age and resting heart rate."}, {"start": 46.0, "text": "Typically, people use 5 heart rate zones to track their effort level in a workout."}, {"start": 52.0, "text": "Each heart rate sample, like 135 beats per minute,"}, {"start": 56.0, "text": "falls into the boundaries of a particular zone, like Zone 3,"}, {"start": 60.0, "text": "which indicates the intensity level."}, {"start": 64.0, "text": "These ranges can be a guide to plan or track exertion levels in activities"}, {"start": 69.0, "text": "like running, cycling, high intensity interval training and rowing."}, {"start": 76.0, "text": "Similarly, cycling power zones,"}, {"start": 79.0, "text": "help cycling enthusiasts measure their power output in watts,"}, {"start": 83.0, "text": "based on a personalized functional threshold power."}, {"start": 87.0, "text": "In iOS 27 and watchOS 27,"}, {"start": 89.0, "text": "heart rate and cycling power zones support has been integrated into HealthKit."}, {"start": 95.0, "text": "I have a sample app that can create,"}, {"start": 97.0, "text": "track, and provide a summary for workout sessions."}, {"start": 100.0, "text": "Throughout this session,"}, {"start": 102.0, "text": "I'll show you how you can incorporate workout zones into your app,"}, {"start": 105.0, "text": "using this sample app as a guide."}, {"start": 108.0, "text": "In this session, I will demonstrate adding heart rate zone support to the sample app."}, {"start": 113.0, "text": "When adopting cycling power zones in your app,"}, {"start": 116.0, "text": "you will find they follow a similar structure."}, {"start": 118.0, "text": "In this session, I'll go over -"}, {"start": 121.0, "text": "accessing zone data from completed workouts,"}, {"start": 124.0, "text": "registering for live zone change updates during a workout session,"}, {"start": 129.0, "text": "accessing people's preferred zone configurations,"}, {"start": 132.0, "text": "and how to provide custom zone configurations for workouts in your app."}, {"start": 137.0, "text": "Workout zones turn raw data, like heart rate samples,"}, {"start": 141.0, "text": "into actionable training guidance."}, {"start": 143.0, "text": "Many workout plans have intensity goals for each workout."}, {"start": 147.0, "text": "For endurance training, a person might want to stay below a certain threshold."}, {"start": 151.0, "text": "However, an interval training exercise might require a person to stay at,"}, {"start": 155.0, "text": "or above a specific level, for a period of time."}, {"start": 159.0, "text": "Zones also help with recovery and load balance."}, {"start": 163.0, "text": "For instance, a workout that is largely spent in higher zones,"}, {"start": 166.0, "text": "like zone 4, or zone 5, suggests a hard effort."}, {"start": 171.0, "text": "You can use this information,"}, {"start": 172.0, "text": "to offer guidance, or classify the intensity of a workout."}, {"start": 177.0, "text": "With workout zones now integrated into HealthKit,"}, {"start": 180.0, "text": "people can share heart rate and cycling power zone information,"}, {"start": 183.0, "text": "directly with your app."}, {"start": 185.0, "text": "The time in each zone is automatically calculated by HealthKit,"}, {"start": 189.0, "text": "based on incoming samples during the workout."}, {"start": 193.0, "text": "Workout zones,"}, {"start": 194.0, "text": "follow a similar authorization flow as other HealthKit data types."}, {"start": 198.0, "text": "Before accessing workout zone data,"}, {"start": 200.0, "text": "request HealthKit authorization for the relevant quantity types."}, {"start": 204.0, "text": "In this case workouts, heart rate, and cycling power."}, {"start": 209.0, "text": "Let's look at how you can retrieve heart rate zones"}, {"start": 211.0, "text": "from a completed workout in HealthKit."}, {"start": 214.0, "text": "My app currently tracks live workouts"}, {"start": 216.0, "text": "and displays a summary, once the workout completes."}, {"start": 219.0, "text": "To help people visualize their intensity and effort,"}, {"start": 222.0, "text": "I now want to display the total time spent,"}, {"start": 224.0, "text": "in each heart rate zone for the completed workout."}, {"start": 227.0, "text": "I can use the HealthKit APIs,"}, {"start": 229.0, "text": "to help graph out the time in each heart rate zone in my app's summary view."}, {"start": 235.0, "text": "To retrieve zone data after a workout,"}, {"start": 237.0, "text": "access the zoneGroupsByType dictionary,"}, {"start": 239.0, "text": "on either the HKWorkout or an individual HKWorkoutActivity,"}, {"start": 243.0, "text": "passing the appropriate HKQuantityType."}, {"start": 246.0, "text": "Here I want heart rate zones,"}, {"start": 248.0, "text": "so I will supply the heart rate quantity type."}, {"start": 251.0, "text": "In iOS 27 and watchOS 27,"}, {"start": 253.0, "text": "HealthKit supports workout zones for heart rate and cycling power,"}, {"start": 257.0, "text": "and they share a similar structure."}, {"start": 259.0, "text": "To receive cycling power zones, just update the associated quantity type."}, {"start": 265.0, "text": "If heart rate zones are available,"}, {"start": 267.0, "text": "this will return an HKWorkoutZoneGroup structure."}, {"start": 270.0, "text": "Let's take a look, at what all this contains."}, {"start": 273.0, "text": "HKWorkoutZoneGroup contains two properties,"}, {"start": 276.0, "text": "a Configuration and an array of zone durations."}, {"start": 280.0, "text": "The HKWorkoutZoneConfiguration,"}, {"start": 282.0, "text": "describes the set of zones and how they were created."}, {"start": 285.0, "text": "It contains the HKQuantityType for the zones."}, {"start": 288.0, "text": "In this case, it's heart rate."}, {"start": 291.0, "text": "The source"}, {"start": 292.0, "text": "is an enum that tells us how the workout zone thresholds were configured."}, {"start": 296.0, "text": "Whether they were created automatically by the System."}, {"start": 299.0, "text": "If they were set manually, by the User in settings."}, {"start": 302.0, "text": "Or if they were custom supplied by an App at the time of the workout."}, {"start": 306.0, "text": "I will go over the Source, in more detail, later in the session."}, {"start": 311.0, "text": "The Configuration also contains an array of zones ordered by the zones' boundaries."}, {"start": 317.0, "text": "Each zone contains an index and a minimum and maximum HKQuantity."}, {"start": 322.0, "text": "The first zone has no lower bound and the last has no upper bound,"}, {"start": 327.0, "text": "ensuring the full range of values is always covered."}, {"start": 330.0, "text": "Zones are guaranteed to be contiguous and non-overlapping."}, {"start": 335.0, "text": "HKWorkoutZoneGroup, also contains an array of zone durations."}, {"start": 341.0, "text": "This is an array,"}, {"start": 343.0, "text": "where each element contains the zone and the time spent in each zone,"}, {"start": 347.0, "text": "ordered by the zone threshold values."}, {"start": 352.0, "text": "I can use these zone durations to populate a graph in my app."}, {"start": 357.0, "text": "When the person ends the workout in my app,"}, {"start": 359.0, "text": "I can chart the time spent in each heart rate zone."}, {"start": 364.0, "text": "Workout zones are available on HKWorkout and HKWorkoutActivity."}, {"start": 368.0, "text": "This means your app"}, {"start": 369.0, "text": "can display zones for the entire duration of the workout all at once,"}, {"start": 373.0, "text": "or, in the case of multi-sport workouts, break them up by individual activities."}, {"start": 379.0, "text": "Some workout plans require a person to stay in or below specific zones."}, {"start": 384.0, "text": "In these cases it would be great if my app could display the current heart rate zone,"}, {"start": 388.0, "text": "and notify the person if it changes,"}, {"start": 391.0, "text": "allowing them to adjust their intensity level,"}, {"start": 393.0, "text": "in order to stay in their target zone."}, {"start": 395.0, "text": "I can use the live workout zone updates to handle zone changes in my app."}, {"start": 401.0, "text": "During a live workout, HealthKit receives incoming heart rate samples."}, {"start": 405.0, "text": "HealthKit processes each heart rate sample to identify the heart rate zone."}, {"start": 409.0, "text": "When there's a change, like from Zone 2 to Zone 3,"}, {"start": 413.0, "text": "HealthKit sends a notification to your app, as the samples are processed."}, {"start": 418.0, "text": "HKLiveWorkoutBuilderDelegate"}, {"start": 420.0, "text": "is the protocol apps use to receive updates about a live workout."}, {"start": 424.0, "text": "The workout is tracked by HealthKit."}, {"start": 426.0, "text": "When something important happens,"}, {"start": 428.0, "text": "like a new activity begins, or a data type that I'm tracking updates,"}, {"start": 432.0, "text": "HealthKit passes that update to my delegate,"}, {"start": 435.0, "text": "and I can make changes in my app,"}, {"start": 438.0, "text": "like update the UI."}, {"start": 439.0, "text": "To process changes in heart rate zone,"}, {"start": 442.0, "text": "I'll use the didUpdateWorkoutZone method."}, {"start": 445.0, "text": "Each update will include the Current zone and Previous zone."}, {"start": 449.0, "text": "Updates are only sent when the Current zone changes, like from Zone 2 to Zone 3,"}, {"start": 454.0, "text": "as well as the zone group,"}, {"start": 456.0, "text": "containing the entire zone configuration and the current total time in each zone."}, {"start": 462.0, "text": "Finally, it includes a timestamp for the last sample processed."}, {"start": 466.0, "text": "This is helpful to display a running timer of the time in the current zone."}, {"start": 471.0, "text": "Once my delegate processes the zone update,"}, {"start": 473.0, "text": "I make changes in my app, to highlight the new current zone."}, {"start": 478.0, "text": "I'll adopt this in my app, so I can handle zone changes within a workout."}, {"start": 484.0, "text": "In my app, I can now highlight the person's current zone"}, {"start": 487.0, "text": "and notify them, if their current zone changes."}, {"start": 491.0, "text": "By default, HealthKit uses the preferred workout zone thresholds,"}, {"start": 494.0, "text": "configured in Health Settings."}, {"start": 496.0, "text": "With preferred zones,"}, {"start": 498.0, "text": "people receive a consistent experience across apps and devices,"}, {"start": 502.0, "text": "since these zones sync across devices via HealthKit."}, {"start": 507.0, "text": "Preferred zones include those calculated by the system."}, {"start": 511.0, "text": "These zones are periodically calculated, based on user metrics, if available."}, {"start": 516.0, "text": "For instance, heart rate zones are automatically calculated,"}, {"start": 520.0, "text": "based on factors such as the person's age and resting heart rate."}, {"start": 525.0, "text": "Preferred zones can also be manually configured in Health Settings."}, {"start": 529.0, "text": "Before starting a zone workout,"}, {"start": 531.0, "text": "make sure the person has a preferred zone configuration set."}, {"start": 534.0, "text": "You can query for the preferred zone configurations"}, {"start": 536.0, "text": "on either the HKHealthStore, or the HKWorkoutBuilder."}, {"start": 540.0, "text": "Custom zones are the right choice,"}, {"start": 542.0, "text": "when your app has specific zone definitions"}, {"start": 544.0, "text": "that differ from zone preferences in Health Settings,"}, {"start": 547.0, "text": "such as a training platform with a proprietary zone model."}, {"start": 551.0, "text": "I can use these two concepts in my app,"}, {"start": 554.0, "text": "to provide a custom set of heart rate zones,"}, {"start": 556.0, "text": "if they have not been configured directly."}, {"start": 559.0, "text": "First, I can check if a preferred heartRate zoneConfiguration has been set."}, {"start": 564.0, "text": "If not, I can use an array of zone thresholds,"}, {"start": 567.0, "text": "and the associated HKUnit to create an array of HKQuantity zone boundaries."}, {"start": 574.0, "text": "I can use the boundaries and the heartRate quantityType,"}, {"start": 577.0, "text": "to create the default HKWorkoutZoneConfiguration."}, {"start": 581.0, "text": "The zone boundary units must match"}, {"start": 583.0, "text": "and be compatible with the zone configurations quantity type."}, {"start": 586.0, "text": "HealthKit creates zones based on the provided boundaries."}, {"start": 590.0, "text": "The first zone starts at 0 and the last is unbounded."}, {"start": 594.0, "text": "Between 3 and 9 zones are required."}, {"start": 598.0, "text": "Next, I will provide the custom configuration to the HKWorkoutBuilder."}, {"start": 603.0, "text": "Custom zones must be added to the builder"}, {"start": 605.0, "text": "before calling beginCollection in your app."}, {"start": 608.0, "text": "There are a few important things to keep in mind,"}, {"start": 610.0, "text": "when using custom zone configurations."}, {"start": 612.0, "text": "Custom zone configurations are only saved within the context of the workout."}, {"start": 617.0, "text": "Your app is responsible for saving,"}, {"start": 619.0, "text": "and syncing custom zone configurations, if needed."}, {"start": 622.0, "text": "Workout zone configurations can contain varying thresholds"}, {"start": 626.0, "text": "and numbers of zones."}, {"start": 627.0, "text": "This is common for cycling power zones."}, {"start": 630.0, "text": "For instance, the system defaults to 6 zones,"}, {"start": 632.0, "text": "but some training apps use 5 and others use 7 or 8."}, {"start": 637.0, "text": "This is important to keep in mind if your app compares efforts"}, {"start": 640.0, "text": "with different numbers of zones."}, {"start": 643.0, "text": "If your app compares time-in-zone across multiple workouts,"}, {"start": 647.0, "text": "with different amounts of zones,"}, {"start": 649.0, "text": "this zone information, can immediately be compared."}, {"start": 652.0, "text": "For instance, zone 3 in a 5-zone workout,"}, {"start": 655.0, "text": "may reflect different values than zone 3 in a 7-zone workout."}, {"start": 660.0, "text": "Each zone represents a different range of values."}, {"start": 665.0, "text": "Instead, make sure to normalize zones,"}, {"start": 668.0, "text": "based on each workout's number of zones and their boundaries."}, {"start": 672.0, "text": "Do this by taking the original samples on the workout,"}, {"start": 675.0, "text": "then sorting them into the appropriate number of buckets for your app."}, {"start": 679.0, "text": "In this case, 7!"}, {"start": 682.0, "text": "Workout zones enable richer, more actionable fitness apps."}, {"start": 686.0, "text": "Whether your building a postworkout summary screen,"}, {"start": 688.0, "text": "a live coaching experience, or a long-term training dashboard,"}, {"start": 691.0, "text": "HealthKit offers a simplified interface for your app to access this data."}, {"start": 697.0, "text": "To get started."}, {"start": 698.0, "text": "Adopt the workout zones API in your app."}, {"start": 701.0, "text": "You can use the provided sample app as a guide."}, {"start": 704.0, "text": "Chart or graph zone data in your app,"}, {"start": 707.0, "text": "so people can visualize their workout effort."}, {"start": 710.0, "text": "And handle live zone changes, to keep people informed,"}, {"start": 713.0, "text": "as their intensity levels adjusts throughout a workout."}, {"start": 717.0, "text": "I love using your apps to help me pursue my fitness goals."}, {"start": 720.0, "text": "Thank you for being a part of the developer community"}, {"start": 723.0, "text": "and empowering people to take charge of their health."}, {"start": 726.0, "text": "Thank you for watching."}]}