Highlight
watchOS 8 extends Always-On Display from watch faces to third-party apps. Combined with the
isLuminanceReducedenvironment value andTimelineView, watch apps stay visible when the wrist is down while controlling update frequency and protecting privacy.
Core Content
You built an Apple Watch app. When users raise their wrist, everything works. But when they lower it, the screen either goes black or shows a blurred interface—that was watchOS 7 behavior.
watchOS 8 changes that.
Starting in watchOS 8, third-party apps can stay visible in Always-On state. Apple Watch Series 5 and later support this. Users can glance at app content without raising their wrist.
That raises two immediate questions:
First, how do you design UI at low brightness?
Second, how do you update content when the wrist is down?
Apple answers both with a set of APIs.
Detailed Content
isLuminanceReduced: Sensing Always-On State
(02:40)
watchOS 8 introduces a new SwiftUI environment property:
@Environment(\.isLuminanceReduced) var isLuminanceReduced
When the watch enters Always-On state, the system automatically lowers screen brightness. isLuminanceReduced becomes true, and your app can adjust UI accordingly.
You can simulate this in Xcode Preview:
ContentView().environment(\.isLuminanceReduced, true)
(03:00)
Key points:
@Environmentreads state from the system environmentisLuminanceReducedistrueat low brightness when the wrist is down- Preview and Simulator support simulating this state; the Simulator window has a dedicated button for wrist-down events
The session shows three real apps’ design approaches.
Fitbod keeps interface structure consistent, only dimming secondary text further.
AnyList removes list item background colors, dims secondary info like “Greek yogurt flavor” and “milk quantity,” and makes primary titles stand out.
Pandora removes album artwork, uses dark outlines instead, dims the bottom control bar, and keeps only song title and artist name clearly visible.
(03:50)
Sensitive information needs handling too. Bank balances and personal health data should be blurred or hidden when the wrist is down. SwiftUI’s .redacted(reason:) modifier shares the same model as widgets.
Animations should reset to the first frame or a static state—don’t pause mid-animation.
TimelineView: Controlling Update Cadence When Wrist Is Down
(06:59)
Apps with active sessions (like workouts or audio playback) can update UI at most once per second when the wrist is down. Apps without active sessions: at most once per minute.
SwiftUI’s TimelineView handles this timed refresh. It takes a schedule and constructs views over time.
watchOS 8 ships five built-in schedules:
EveryMinute: Updates every minute, aligned to the system clock, firing on the minutePeriodic: Custom interval, e.g. every two minutes, not aligned to the system clockExplicit: Updates at specified time points, good for precise timingCustom: Mixed strategy, e.g. every minute for 59 minutes, then every second for the last 60 secondsAnimation: Animation-specific
(07:18)
A typical TimelineView usage:
TimelineView(.everyMinute) { context in
Text(context.date, style: .time)
}
Key points:
TimelineView’s closure receives aTimelineViewContextwith the current date- Without an active session, requesting updates faster than once per minute may be attempted but isn’t guaranteed
- By default the system returns to the watch face after two minutes, but users can change this in Settings—apps should be ready to stay visible longer
HealthKit Background Delivery
(08:58)
watchOS 8 brings HealthKit background delivery from iOS to Watch apps. Setup matches iOS:
let query = HKObserverQuery(
sampleType: heartRateType,
predicate: nil
) { query, completionHandler, error in
fetchLatestHeartRateData()
completionHandler()
}
healthStore.execute(query)
healthStore.enableBackgroundDelivery(
for: heartRateType,
frequency: .immediate
) { success, error in
// Enable background delivery
}
(09:13)
Key points:
HKObserverQuerylistens for data changesenableBackgroundDeliveryenables background wake- Regular apps wake at most once per hour
- If complications are on an active watch face, up to four times per hour
- All wake counts count toward the background refresh budget, capped at four per hour
- Critical data types like fall events, low blood oxygen, and abnormal heart rate deliver immediately
- Other data types deliver hourly or on longer intervals
The HealthKit authorization form was updated to tell users the app will access health data on Apple Watch more frequently.
Bluetooth Device Background Connection
(10:49)
watchOS 4 added direct Bluetooth device connection to Apple Watch. watchOS 8’s new capability is connecting during background refresh.
That means complications can continuously fetch data from Bluetooth accessories—e.g. Qardio’s medical-grade ECG or Sonra Watch’s GPS tracker for soccer players.
Background refresh provides at most four connection opportunities per hour, also counting toward the background refresh budget.
Apple added an expiration handler to WKRefreshBackgroundTask:
func handle(_ backgroundTasks: Set<WKRefreshBackgroundTask>) {
for task in backgroundTasks {
task.expirationHandler = {
// Time running out—clean up connection
}
// Connect Bluetooth device, fetch data
}
}
(12:35)
Key points:
- Initial Bluetooth connection must happen in the foreground; background can’t establish new connections
- After connection is established, background refresh can reconnect to fetch data
- Data processing must complete quickly
expirationHandlerlets you wrap up gracefully before time runs out
Region-Based Location Notifications
(13:15)
watchOS 8 supports UNLocationNotificationTrigger, notifying users when they enter or leave a specified region.
let center = CLLocationCoordinate2D(latitude: 37.7749, longitude: -122.4194)
let region = CLCircularRegion(
center: center,
radius: 200,
identifier: "gym"
)
region.notifyOnEntry = true
let trigger = UNLocationNotificationTrigger(region: region, repeats: false)
let content = UNMutableNotificationContent()
content.title = "Arrived at the gym"
content.body = "Start tracking your workout"
let request = UNNotificationRequest(
identifier: "gym-entry",
content: content,
trigger: trigger
)
UNUserNotificationCenter.current().add(request)
(13:40)
Key points:
CLCircularRegiondefines a circular geofenceUNLocationNotificationTriggerfires notifications based on region- Requires “When in use” location permission
- Users first receive a static notification; full dynamic content appears after tap, protecting privacy
- Region radius should be a few hundred meters; only set locations users clearly care about
Core Location’s location button also comes to watchOS. Tapping grants one-time location permission without a prompt every time.
Other System Enhancements
(15:18)
watchOS 8 also includes notable improvements:
- Sleep respiratory rate: Apple Watch can measure respiratory rate during sleep; apps read it via HealthKit
- AssistiveTouch: Gesture recognition helps users with upper-body differences operate the watch
- Large accessibility text: Larger accessibility text sizes
- Large titles: Scroll views support iOS-style large titles at the top
- Text input improvements: Remembers Scribble/dictation preferences per app; quick switching supported
- Search API:
searchablemodifier supports custom search suggestions - List swipe actions: List items support custom swipe actions (e.g. favorites)
- Button roles: Distinguish button types (e.g. destructive); control prominence and extra haptics
- Canvas API: SwiftUI Canvas comes to watchOS for GPU programmatic drawing
Core Takeaways
-
Build a wrist-down-visible timer app. Use
TimelineView+EveryMinuteschedule so users see remaining time while cooking or exercising with wrist down. Switch to per-second updates for the last 60 seconds. Entry APIs:TimelineView+Customschedule. -
Build a Bluetooth glucometer complication companion. Connect during background refresh, update the watch face complication hourly. Use
expirationHandlerto close connections promptly. Entry APIs:CBCentralManager+WKRefreshBackgroundTask. -
Build a location-based workout reminder app. Remind users to start logging when they arrive at the gym, save data when they leave. Use
UNLocationNotificationTriggerwith a 200-meter geofence. Entry APIs:CLCircularRegion+UNLocationNotificationTrigger. -
Build a sleep respiratory monitoring dashboard. Use watchOS 8’s new sleep respiratory rate data; show trends each morning. Combine with HealthKit background delivery—no manual refresh. Entry APIs:
HKQuantityType(.respiratoryRate) +HKObserverQuery. -
Build an Always-On-friendly music player. Hide album art and playback controls when wrist is down; show only song and artist. Use
isLuminanceReducedto control UI hierarchy per state. Entry APIs:@Environment(\.isLuminanceReduced)+.redacted(reason:).
Related Sessions
- Build a workout app for Apple Watch — Build a workout app from scratch with SwiftUI and HealthKit, including TimelineView and Always-On code
- Connect Bluetooth devices to Apple Watch — Deep dive into full Bluetooth background connection implementation
- There and back again: Data transfer on Apple Watch — Apple Watch data communication strategies, including Watch Connectivity and URLSession
- What’s new in SwiftUI — SwiftUI updates including more TimelineView usage
Comments
GitHub Issues · utterances