Die watchOS-Laufzeit ist ein Vertrag, keine Hintergrundaufgabe
Die Watch-App von Return betreibt einen Meditationstimer mit mehreren Zyklen, der weiterzählen muss, wenn der Benutzer das Handgelenk absenkt.1 Das Pattern, das diese Einschränkung übersteht, ist WKExtendedRuntimeSession in Verbindung mit einem globalen, app-weiten Delegate. Alles andere stirbt in dem Moment, in dem die Watch in den Schlafmodus wechselt.
watchOS ist nicht iOS mit kleinerem Bildschirm. Das Laufzeitmodell ist anders. iOS gewährt einer App ein großzügiges Vordergrundbudget sowie eine schwindende, aber reale Hintergrundlaufzeit über Audio-Sessions, Location-Updates, BGTaskScheduler und eine Handvoll weiterer Möglichkeiten.2 watchOS gewährt der Vordergrund-App nach dem Absenken des Handgelenks ein Budget, das in Sekunden gemessen wird, und danach wird die App suspendiert, sofern sie keinen Laufzeitvertrag mit dem System geschlossen hat. Es gibt keine Möglichkeit nach dem Motto „ich erledige nur kurz etwas im Hintergrund”. Es gibt nur „ich führe ein Workout, eine Achtsamkeitssitzung, einen smarten Wecker, eine Navigationsroute oder eine Health-Monitoring-Aufgabe aus” – und sonst nichts.3
Das Watch-Target von Return ist ein Achtsamkeitstimer. Der Sitzungsvertrag lautet WKBackgroundModes: mindfulness. Die Laufzeit-API ist WKExtendedRuntimeSession. Das Pattern, das die Watch-App von defekt beim Absenken des Handgelenks zu übersteht eine 25-minütige Meditation gebracht hat, ist genau jenes, das dieser Beitrag beschreibt.
TL;DR
- watchOS hat keinen iOS-artigen Hintergrund. Die Vordergrundlaufzeit endet kurz nach dem Absenken des Handgelenks, und nur registrierte Sitzungstypen laufen weiter.
WKExtendedRuntimeSessionist die API-Oberfläche. Apple unterstützt vier Sitzungstypen:self-care,mindfulness,physical-therapyundalarm. Für einen Meditationstimer ist der Sitzungstypmindfulness, deklariert überWKBackgroundModesin derInfo.plist.- Der Sitzungs-Manager muss auf App-Ebene leben, nicht auf View-Ebene. Der View-Lifecycle von SwiftUI dealloziert View-eigene Objekte beim Navigieren; ein deallozierter Sitzungs-Delegate ist eine tote Sitzung, selbst wenn die Sitzung selbst noch läuft.
- Die Callbacks von
WKExtendedRuntimeSessionDelegatesind der Vertrag:didStart,willExpire,didInvalidateWith. Der Expire-Callback feuert, bevor das System eine Invalidierung erzwingt; Apples Beispielcode in „Using extended runtime sessions” beschreibt ihn als die Stelle, an der „Aufgaben abgeschlossen und aufgeräumt werden, bevor die Sitzung endet.”3 - Ein Absenken des Handgelenks ohne aktive erweiterte Sitzung pausiert den Timer. Ein Absenken des Handgelenks mit aktiver erweiterter Sitzung lässt den Timer weiterlaufen. Die Sitzung ist der Unterschied zwischen „ausgeliefertes Produkt” und „defekt bei der zweiten Nutzung.”
Das Hintergrundproblem, das watchOS nicht auf iOS-Art löst
iOS-Apps greifen auf mehrere Hintergrundmöglichkeiten zurück, wenn die App bei ausgeschaltetem Bildschirm weiterlaufen soll:2
- Eine
AVAudioSessionmit der Kategorie.playbackhält eine Audio-App am Leben, während Musik abgespielt wird. CLLocationManager-Hintergrundupdates halten eine Navigations-App mit einem blauen Balken am Leben.BGTaskSchedulerreiht kurze Wartungsarbeiten ein, die das System nach eigenem Zeitplan einplant.- Eine UI-Erweiterung im Vordergrund (Live Activity, CallKit, PushKit) verbindet den App-Prozess mit einer systemgesteuerten Rendering-Oberfläche.
Keine dieser Möglichkeiten hilft auf watchOS in der Weise, wie man vielleicht annehmen würde. Watch-Apps haben nicht denselben Background-Task-Scheduler. Sie haben keinen im Hintergrund laufenden AVAudioSession.playback-Modus, der den Timer in der Stille weiterzählen lässt. Sie haben ein einziges strukturelles Primitiv für „ich möchte weiterlaufen, nachdem der Benutzer das Handgelenk gesenkt hat”, und dieses Primitiv ist WKExtendedRuntimeSession mit einem deklarierten Sitzungstyp.3
Die Sitzungstypen, die Apple für WKExtendedRuntimeSession unterstützt, sind bewusst eng gefasst:3
self-care(kurze Wohlbefindens-Aktivitäten, Vordergrundlaufzeit, 10-Minuten-Limit)mindfulness(stille Meditation, Vordergrundlaufzeit, 1-Stunden-Limit)physical-therapy(Dehnübungen und Bewegungsumfang, Hintergrundlaufzeit, 1-Stunden-Limit)alarm(smarte Wecker, Hintergrundlaufzeit, 30-Minuten-Limit, planbar bis zu 36 Stunden im Voraus überstart(at:))
Workout-Apps nutzen HKWorkoutSession mit dem separaten Hintergrundmodus workout-processing; dieser Pfad ist für tatsächliche Workouts dokumentiert und ist kein WKExtendedRuntimeSession-Typ.4 Der Hintergrundmodus underwater-depth unterstützt Tauch- und Tiefenmessungs-Apps über den Pfad der Tauchsitzungs-API, ebenfalls nicht über WKExtendedRuntimeSession. Eine App kann workout-processing mit einem erweiterten Laufzeit-Sitzungstyp kombinieren, kann aber nicht mehr als einen erweiterten Laufzeittyp pro App auswählen.4
Apps, die in keine dieser Kategorien passen, können WKExtendedRuntimeSession nicht nutzen, um nach dem Absenken des Handgelenks weiterzulaufen. Audio-Apps greifen auf die Audio-Sitzungskategorie AVAudioSession.Category.playback und die Now-Playing-Integration auf einem anderen Codepfad zurück; Navigations-Apps verwenden CLLocationManager-Hintergrundupdates. Die Watch ist kein Allzweckcomputer; sie ist ein Gerät mit Akku-Einschränkungen, die das Laufzeitmodell durchsetzt.
Ein Meditationstimer passt zu mindfulness. Der Vertrag: Hintergrundmodus in der Info.plist deklarieren, WKExtendedRuntimeSession anfordern, die Delegate-Callbacks behandeln, die Sitzung beenden, wenn der Timer endet. Apple dokumentiert das Limit für die Mindfulness-Sitzung mit einer Stunde, wobei das System nach eigenem Ermessen unter thermischer oder Akkubelastung verkürzen kann.3
Das in Return ausgelieferte Pattern
Das Pattern beginnt mit der Deklaration in der Info.plist:4
<key>WKBackgroundModes</key>
<array>
<string>mindfulness</string>
</array>
Diese Modus-Deklaration macht den Sitzungstyp überhaupt erst gültig. Ohne sie schlägt der Aufruf von WKExtendedRuntimeSession().start() stillschweigend fehl, und die App wird beim Absenken des Handgelenks genauso suspendiert wie eine Watch-App ohne jegliche Hintergrundmodi.
Der Sitzungs-Manager selbst muss auf App-Ebene leben. Der View-Lifecycle von SwiftUI ist langlebigen, zustandsbehafteten Objekten nicht zuträglich: @StateObject und @State sind an die View gebunden, die sie besitzt, und ein Navigations-Push, der die View ersetzt, verwirft den Zustand mit ihr. Eine WKExtendedRuntimeSession, deren Delegate mitten in der Sitzung dealloziert wird, stürzt nicht ab; die Sitzung läuft weiter, aber die Delegate-Callbacks (willExpire, didInvalidateWith) erreichen ein freigegebenes Objekt – das bedeutet, die Bereinigung findet nie statt, was wiederum bedeutet, dass der nächste Aufruf von startSession() annimmt, es gebe keine aktive Sitzung, und eine doppelte Sitzung startet.
Das ausgelieferte Pattern ist ein Singleton auf App-Ebene. Der folgende Ausschnitt zeigt die strukturelle Form; in der Produktion wird in jeder Methode zusätzlich Logging zur Beobachtbarkeit ergänzt:
import SwiftUI
import WatchKit
final class WatchSessionManager: NSObject, WKExtendedRuntimeSessionDelegate {
static let shared = WatchSessionManager()
private var session: WKExtendedRuntimeSession?
private override init() {
super.init()
}
var isSessionActive: Bool {
session != nil
}
func startSession() {
guard session == nil else { return }
let newSession = WKExtendedRuntimeSession()
newSession.delegate = self
newSession.start()
session = newSession
}
func endSession() {
guard let existing = session else { return }
existing.invalidate()
session = nil
}
// MARK: - WKExtendedRuntimeSessionDelegate
func extendedRuntimeSessionDidStart(_ session: WKExtendedRuntimeSession) {}
func extendedRuntimeSessionWillExpire(_ session: WKExtendedRuntimeSession) {
// Apple's "about to expire / finish and clean up" hook
}
func extendedRuntimeSession(
_ session: WKExtendedRuntimeSession,
didInvalidateWith reason: WKExtendedRuntimeSessionInvalidationReason,
error: Error?
) {
self.session = nil
}
}
Über die Protokollkonformität hinaus sind drei strukturelle Details entscheidend.
Die Instanz static let shared wird über statischen Speicher für die gesamte Prozesslebensdauer der Watch-App gehalten; ARC wird sie nicht deallozieren. Was die Bindung auf App-Ebene erkauft, ist nicht zusätzliche Retention, sondern ein stabiler Beobachtungspunkt. Das Bug-Pattern, das dies verhindert: Ein Sitzungs-Manager, der nur von einer kurzlebigen View gehalten wird, die mitten in der Sitzung gepoppt wird, wobei die View stirbt, aber static let shared überlebt – mit dem Nebeneffekt, dass jeder mit @StateObject umhüllte Manager seinen Beobachtungszyklus verliert und nicht mehr korrekt neu rendert. Verwenden Sie das Singleton in Verbindung mit einem @Observable-Accessor auf App-Ebene, damit die UI weiterhin die kanonische Instanz beobachtet.
Die Eigenschaft session ist der Schutz vor doppelten Sitzungen. Ein Timer mit einem „Neu starten”-Button kann startSession() aus mehreren Pfaden aufrufen; die Prüfung guard session == nil ist die Sperre. Zwei gleichzeitige erweiterte Sitzungen verursachen unvorhersehbares Verhalten: Manchmal gelingt die zweite und die erste wird verwaist, manchmal schlägt der Start-Aufruf stillschweigend fehl. Die Single-Session-Invariante verhindert die gesamte Klasse dieser Fehler.
Die Delegate-Callbacks loggen, handeln aber selten. Der didStart-Callback feuert einmal pro Sitzung und ist ein nützlicher Hook für Beobachtbarkeit; der willExpire-Callback feuert, bevor das System eine Invalidierung erzwingt, und ist die Stelle, an der Apples Beispiel erwartet, dass die App „Aufgaben abschließt und aufräumt, bevor die Sitzung endet”; der didInvalidateWith-Callback ist die Stelle, an der die Sitzungsreferenz geleert wird, damit der nächste Aufruf von startSession() funktioniert. Das ausgelieferte Pattern lautet Callbacks aktualisieren den Zustand, die Zustandsmaschine erledigt die Arbeit, nicht Callbacks erledigen die Arbeit direkt.
Der Timer-Manager ruft den Sitzungs-Manager bei jedem Übergang auf, der ändert, ob der Timer aktiv zählt:
@Observable final class WatchTimerManager {
func start() {
startExtendedSession() // -> WatchSessionManager.shared.startSession()
// ... start the timer state machine ...
}
func pause() {
timer?.invalidate()
isRunning = false
endExtendedSession() // -> WatchSessionManager.shared.endSession()
}
func reset() {
// ... clear timer state ...
endExtendedSession()
}
private func completeCycle() {
// ... last cycle handling ...
endExtendedSession() // ends on final completion
}
}
Die Sitzung endet beim Pausieren, beim Zurücksetzen und beim Abschluss des letzten Zyklus. Die Produktbegründung: Eine pausierte Meditation muss nicht weiterhin das Laufzeitbudget beanspruchen, das das System unter mindfulness gewährt; eine Wiederaufnahme aus der Pause heraus erwirbt eine frische Sitzung. Der Produktnachteil ist, dass eine bei abgesenktem Handgelenk pausierte Sitzung nicht allein durch Anheben des Handgelenks fortgesetzt werden kann; der Benutzer muss die App wieder in den Vordergrund holen, um fortzusetzen. Der Produktgewinn ist, dass die Akkukosten eines pausierten Timers auf null fallen und das System keine veraltete Sitzung sieht.
Das Absenken des Handgelenks ist der Test
Tests von watchOS im Simulator sind eine höfliche Fiktion. Der Simulator erzwingt das Laufzeitmodell beim Absenken des Handgelenks nicht so, wie es eine echte Apple Watch tut. Der Simulator hält die App im Vordergrund, solange das Simulator-Fenster den Fokus hat; eine erweiterte Laufzeitsitzung im Simulator sieht identisch zu gar keiner Sitzung aus, weil die Vordergrund-App ohnehin weiterläuft.
Der eigentliche Test findet auf einer echten Apple Watch statt:5
- Den Timer starten.
- Das Handgelenk absenken (oder die Seitentaste drücken, um den Bildschirm zu sperren).
- 30 Sekunden warten.
- Das Handgelenk wieder anheben.
Ohne aktive erweiterte Laufzeitsitzung wird die Watch-App suspendiert; der Timerzustand ist im Moment des Absenkens eingefroren und nimmt aus diesem eingefrorenen Zustand wieder auf. Bei einer 5-minütigen Meditation, in der der Benutzer die Augen geschlossen hat, ist der Bug unsichtbar, bis der Timer um genau die Zeitspanne falsch geht, in der die Augen geschlossen waren.
Mit einer aktiven erweiterten Laufzeitsitzung zählt der Timer weiter. Das Anheben des Handgelenks zeigt den Timer an der korrekten verstrichenen Position. Das Audiosignal (sofern der Timer eines beim Abschluss abspielt) feuert zur korrekten Wanduhrzeit, nicht zur Zeit des Anhebens.
Das Wrist-Drop-Szenario war der Bug, mit dem Returns erster Watch-Build ausgeliefert wurde, und das Singleton-Refactoring hat ihn behoben. Der Fix ist das Singleton-Pattern oben; der Bug war eine WatchSessionManager-Instanz, die von einer SwiftUI-View gehalten wurde, die beim Navigations-Push dealloziert wurde. Die Sitzung lief technisch auf der Systemseite, aber der Delegate war freigegeben; der nächste Aufruf zum Sitzungsstart war stillschweigend ein No-Op, weil die session-Eigenschaft des Managers auf einem mittlerweile toten Objekt gesetzt worden war. Tests auf echten Geräten bringen den Fehler in Sekunden zum Vorschein. Tests im Simulator bringen ihn nie zum Vorschein.
Was die Delegate-Callbacks Ihnen tatsächlich sagen
WKExtendedRuntimeSessionInvalidationReason listet die Möglichkeiten auf, wie eine Sitzung enden kann:6
| Grund | Wann er auftritt |
|---|---|
none |
Die Sitzung wurde explizit durch den Aufruf von invalidate() durch die App invalidiert |
sessionInProgress |
Eine Sitzung desselben Typs läuft bereits |
expired |
Das systemseitige Zeitlimit wurde erreicht |
resignedFrontmost |
Eine andere App wurde im Vordergrund aktiv, während die Sitzung lief |
suppressedBySystem |
Das System hat die Sitzung unterdrückt (geringe Energie, thermische Belastung) |
error |
Ein nicht behebbarer Fehler ist aufgetreten; prüfen Sie den error-Parameter |
Die Gründe, die für das Produktdesign relevant sind:
expired bedeutet, dass das systemseitige Zeitlimit erreicht wurde. Apple dokumentiert das Limit der Mindfulness-Sitzung mit einer Stunde;3 Returns längste Meditationsdauer beträgt 60 Minuten, was der dokumentierten Obergrenze entspricht. Eine 90-minütige Meditation kann nicht innerhalb einer einzelnen Mindfulness-Sitzung abgeschlossen werden: Der Timer würde an der Stundenmarke mitten in der Sitzung sterben. Die Produktentscheidung lautet, die verfügbaren Dauern auf das zu begrenzen, was das Laufzeitmodell dokumentiert liefert, und nicht auf Systemtoleranz zu spekulieren.
resignedFrontmost bedeutet, dass der Benutzer eine andere Watch-App geöffnet hat und Ihre Sitzung verloren hat. Watch-Benutzer wechseln gerne per Wischgeste zu einer anderen App und vergessen sie dann. Die Produktentscheidung lautet, entweder bei Resign zu pausieren (Zustand wird erhalten, der Benutzer kann zurückkehren) oder bei Resign zu beenden (Sitzung vorbei, der Benutzer erhält ein Signal „Sie haben vorzeitig aufgehört”). Return entscheidet sich für Pausieren bei Resign, damit der Benutzer mitten in der Meditation einen Anruf entgegennehmen und danach zurückkehren kann.
suppressedBySystem ist die höfliche Version von „die Watch ist heiß.” Ein watchOS-Gerät unter thermischer Belastung oder mit niedrigem Akkustand kann eine erweiterte Laufzeitsitzung auch ohne Fehlverhalten der App widerrufen. Der Sitzungs-Manager muss diesen Fall sauber behandeln: die Referenz löschen, eine nicht-blockierende Warnung anzeigen und nicht in einen Zustand geraten, in dem er versucht, eine Sitzung neu zu starten, die das System gerade abgelehnt hat.
Der willExpire-Callback feuert, wenn die Sitzung kurz vor dem Ablauf steht; Apples Beispiel beschreibt ihn als den Moment, „Aufgaben abzuschließen und aufzuräumen, bevor die Sitzung endet.”3 Der Callback ist die Stelle, an der eine App einen finalen Zustands-Snapshot schreiben, ein abschließendes Audiosignal abspielen oder eine UI mit der Meldung „Sitzung endet bald” präsentieren kann. Return loggt den Callback heute lediglich; reichhaltigere Bereinigung (HealthKit-Logeintrag, Audio-Fade-out) findet auf den Reset- und Abschlusspfaden des Timers statt und steht auf der Liste was ich anders bauen würde für das willExpire-Fenster.
Was ich anders bauen würde
Zwei Dinge, falls Return von vorne beginnen würde.
HKWorkoutSession für jede Sitzung verwenden, deren Wert mit HealthKit-Integration steigt. Ein Meditationstimer steht an der Grenze zwischen mindfulness und workout-processing. Mindfulness war die richtige Wahl für v1, weil das Datenmodell einfacher ist und die Benutzererwartung lautet „das ist Meditation, kein Workout.” HKWorkoutSession bietet eine granularere HealthKit-Integration (Sitzungsstart, Sitzungsende, Segmente, Ereignisse) und bietet eine reichhaltigere LiveWorkoutBuilder-Schnittstelle zum Sammeln von Daten. Das architektonische Urteil, keine dokumentierte Apple-Garantie: Für eine App, deren Wert von detaillierter Sitzungstelemetrie abhängt, behandelt der Workout-Session-Pfad Strukturen, die WKExtendedRuntimeSession nicht abdeckt.
Eine Beobachtungsoberfläche für den Sitzungszustand vom ersten Tag an einbauen. Die erste Version von Return loggte Sitzungsereignisse in die Konsole. Die zweite Version fügte zur Fehlersuche eine On-Device-Sichtbarkeit des Sitzungszustands hinzu. Die dritte würde einen Entwicklermodus-Schalter bereitstellen, der dem Benutzer den Verlauf der Sitzungsgründe sichtbar macht, wenn etwas schiefgeht, anstatt eine Sitzungsinvalidierung als Black Box zu behandeln. Die watchOS-Laufzeit ist undurchsichtig; die Debug-Oberfläche muss das ausgleichen.
Wann WKExtendedRuntimeSession die falsche Antwort ist
Drei Fälle, in denen der Sitzungstyp nicht passt:
Workouts, die Segmentmarker, Herzfrequenz-Streams oder aktive Kalorienverfolgung benötigen. Verwenden Sie HKWorkoutSession direkt mit einem HKLiveWorkoutBuilder. Die Workout-API ist Apples dokumentierter Pfad für tatsächliche Workouts (und Gehmeditationen oder anstrengende Aktivitäten); WKExtendedRuntimeSession ist der dokumentierte Pfad für Nicht-Workout-Sitzungen wie Achtsamkeit oder Wecker. Eine Meditations-App benötigt kein Workout; eine Couch-to-5K-App schon.
Audiowiedergabe, die eine Now-Playing-Oberfläche benötigt. Verwenden Sie eine AVAudioSession, die für die Wiedergabe konfiguriert ist, zusammen mit watchOS-Audio-Sitzungs-Entitlements; die Now-Playing-Integration plus die System-Wiedergabeoberfläche ist das, was Audio-Apps wollen, und der Audiopfad ist vollständig getrennt von WKExtendedRuntimeSession. WKExtendedRuntimeSession bietet weder Now Playing noch das System-Audio-Routing.
Langlaufende Datensynchronisation ohne Benutzerwahrnehmung. Verwenden Sie WKApplicationRefreshBackgroundTask für periodische Aktualisierungsfenster, die das System einplant. Der Benutzer ist nicht in der App; die App muss nicht weiterlaufen; sie muss nur kurz aufwachen und aktualisieren. Die Modelle für Background-Task und erweiterte Laufzeitsitzung dienen sehr unterschiedlichen Zwecken.
Was das Pattern für Apps bedeutet, die auf watchOS 11+ ausgeliefert werden
Drei Erkenntnisse.
-
Das Watch-Laufzeitmodell ist Opt-in. Wählen Sie einen Sitzungstyp und leben Sie innerhalb seiner Regeln. Apps, die versuchen, „allgemeine Hintergrundarbeit” auf watchOS zu erledigen, werden verlieren. Wählen Sie
mindfulness,workout-processing,self-care,physical-therapy,alarmoderunderwater-depth, und gestalten Sie die Benutzererfahrung rund um das Laufzeitbudget, das mit dem von Ihnen gewählten Sitzungstyp einhergeht. -
Der Sitzungs-Delegate muss auf App-Ebene leben. Der View-Lifecycle von SwiftUI schützt langlebige, zustandsbehaftete Objekte nicht. Ein
static let shared-Singleton, das auf der@main App-Ebene gebunden ist, ist das kleinste Pattern, das Navigations-Pushs, View-Ersetzungen und das normale Deallokationsverhalten von SwiftUI übersteht. -
Auf echter Hardware testen. Der Simulator erzwingt das Laufzeitmodell beim Absenken des Handgelenks nicht. Der Bug, den eine Watch-App im Simulator nicht testen kann, ist der Bug, den sie an die Benutzer ausliefert.
Lesen Sie diesen Beitrag zusammen mit meinen früheren Texten zur selben Familie von Apps: plattformübergreifendes SwiftUI-Shipping (Return wird auf iPhone, iPad, Watch, Mac und Apple TV ausgeliefert); die Live-Activities-Zustandsmaschine (die iOS-seitige Oberfläche für denselben Timer); HealthKit-Patterns (wo die Mindfulness-Sitzungen der Watch in den Health-Daten des Benutzers landen). Die vollständige Sammlung finden Sie im Apple Ecosystem Series Hub. Für einen breiteren Kontext zu iOS mit AI-Agenten siehe den iOS Agent Development Guide.
FAQ
Was ist eine watchOS Extended Runtime Session?
Eine watchOS Extended Runtime Session (WKExtendedRuntimeSession) ist die API, die eine Watch-App nutzt, um nach dem Absenken des Handgelenks weiterzulaufen. Die Sitzung muss einen Typ deklarieren (mindfulness, workout-processing, alarm usw.) – über WKBackgroundModes in der Info.plist. Ohne aktive erweiterte Sitzung suspendiert watchOS die App kurz nach dem Absenken des Handgelenks.
Warum stoppt mein watchOS-Timer das Zählen, wenn der Benutzer das Handgelenk absenkt?
Die Watch-App wird kurz nach dem Absenken des Handgelenks suspendiert, sofern keine aktive WKExtendedRuntimeSession eines unterstützten Typs läuft. Ein Timer-Manager, der keine solche Sitzung startet, wird seine Hintergrundlaufzeit gekappt sehen, und der Timerzustand friert im Moment des Absenkens ein, bis der Benutzer das Handgelenk wieder anhebt.
Was ist der Unterschied zwischen WKExtendedRuntimeSession und HKWorkoutSession?
WKExtendedRuntimeSession ist die universelle erweiterte Laufzeit-API für Nicht-Workout-Sitzungen wie Achtsamkeit, Wecker oder Selbstfürsorge. HKWorkoutSession ist die API für tatsächliche Workouts; sie integriert sich in HealthKit, unterstützt Segmentmarker und ist der dokumentierte Pfad für Gehmeditationen oder anstrengende Aktivitäten. Achtsamkeits-Apps ohne Workout-würdige Telemetrie nutzen die erste; Workout-Apps nutzen die zweite.
Kann das System meine Extended Runtime Session widerrufen?
Ja. WKExtendedRuntimeSessionInvalidationReason umfasst expired (systemseitiges Zeitlimit erreicht), resignedFrontmost (eine andere Watch-App wurde im Vordergrund aktiv) und suppressedBySystem (geringe Energie oder thermische Belastung). Der Sitzungs-Manager muss jeden Fall sauber behandeln: Die Referenz wird geleert, der Timerzustand reagiert angemessen, und der nächste Sitzungs-Start-Aufruf funktioniert korrekt.
Wo sollte der Sitzungs-Manager in einer SwiftUI-watchOS-App leben?
Auf App-Ebene, als Singleton, der vom @main App-Struct gebunden wird. Der View-bezogene Zustand von SwiftUI (@State, @StateObject) wird bei Navigations-Pushs, View-Ersetzungen oder dem Wechsel der App in den Hintergrund dealloziert. Ein View-eigener Sitzungs-Delegate, der mitten in der Sitzung freigegeben wird, führt dazu, dass die Sitzungsreferenz leckt und nachfolgende Sitzungen nicht sauber gestartet werden können.
Quellen
-
Eigenes Projekt Return, ein SwiftUI-Meditationstimer, der am 21. April 2026 im App Store veröffentlicht wurde, verfügbar für iPhone, iPad, Mac, Apple Watch und Apple TV. Die Watch-App nutzt
WKExtendedRuntimeSessionmit dem Hintergrundmodusmindfulnessfür die Laufzeit des Zyklus-Timers. ↩ -
Apple Developer, “About the background execution sequence”. iOS-seitige Hintergrundlaufzeit-Möglichkeiten (Audio-Sessions, Standort, BGTaskScheduler) und ihre Unterschiede zu watchOS. ↩↩
-
Apple Developer, “WKExtendedRuntimeSession”. Sitzungstypen, Lifecycle, Delegate-Callbacks, Laufzeitlimits und der Info.plist-Schlüssel
WKBackgroundModes. ↩↩↩↩↩↩↩ -
Apple Developer, “Information Property List: WKBackgroundModes”. Unterstützte Sitzungstyp-Strings:
workout-processing,mindfulness,self-care,physical-therapy,alarm,underwater-depth. ↩↩↩ -
Apple Developer, “Building a watchOS app” und der Leitfaden zum Testen von WatchKit. Das Laufzeitverhalten auf echten Geräten ist im watchOS-Simulator nicht reproduzierbar; der Simulator erzwingt die Suspendierung beim Absenken des Handgelenks nicht. ↩
-
Apple Developer, “WKExtendedRuntimeSessionInvalidationReason”. Enum-Fälle:
none,sessionInProgress,expired,resignedFrontmost,suppressedBySystem,error. ↩