← Wszystkie wpisy

Środowisko uruchomieniowe watchOS to kontrakt, a nie zadanie w tle

Aplikacja Watch w Return obsługuje wielocykliczny zegar medytacji, który musi nadal odliczać, gdy użytkownik opuści nadgarstek.1 Wzorzec, który przetrwa to ograniczenie, to WKExtendedRuntimeSession plus globalny delegat o zasięgu aplikacji. Wszystko inne umiera w chwili, gdy zegarek przechodzi w stan uśpienia.

watchOS to nie iOS z mniejszym ekranem. Model środowiska uruchomieniowego jest inny. iOS zapewnia aplikacji hojny budżet pierwszego planu oraz malejące, ale realne środowisko uruchomieniowe w tle poprzez sesje audio, aktualizacje lokalizacji, BGTaskScheduler oraz garść innych mechanizmów.2 watchOS przyznaje aplikacji na pierwszym planie budżet liczony w sekundach po opuszczeniu nadgarstka, a po jego upływie aplikacja zostaje zawieszona, chyba że podpisała z systemem kontrakt środowiska uruchomieniowego. Nie ma mechanizmu „po prostu robię coś w tle”. Jest „prowadzę trening, sesję uważności, inteligentny budzik, trasę nawigacyjną lub zadanie monitorowania zdrowia” — i nic poza tym.3

Cel Watch w Return to zegar uważności. Kontrakt sesji to WKBackgroundModes: mindfulness. Powierzchnią środowiska uruchomieniowego jest WKExtendedRuntimeSession. Wzorzec, który przeprowadził aplikację Watch od stanu zepsutego po opuszczeniu nadgarstka do przetrwania 25-minutowej medytacji, jest tym, który opisuje ten wpis.

TL;DR

  • watchOS nie ma trybu w tle w stylu iOS. Środowisko uruchomieniowe pierwszego planu kończy się wkrótce po opuszczeniu nadgarstka, a tylko zarejestrowane typy sesji są kontynuowane.
  • WKExtendedRuntimeSession jest powierzchnią środowiska uruchomieniowego. Apple obsługuje cztery typy sesji: self-care, mindfulness, physical-therapy oraz alarm. W przypadku zegara medytacji typem sesji jest mindfulness, deklarowany przez WKBackgroundModes w pliku Info.plist.
  • Menedżer sesji musi mieć zasięg aplikacji, a nie zasięg widoku. Cykl życia widoku w SwiftUI dealokuje obiekty należące do widoku przy nawigacji; zdealokowany delegat sesji to martwa sesja, nawet jeśli sama sesja nadal działa.
  • Wywołania zwrotne WKExtendedRuntimeSessionDelegate są kontraktem: didStart, willExpire, didInvalidateWith. Wywołanie zwrotne wygaśnięcia odpala się przed wymuszonym unieważnieniem przez system; przykładowy kod Apple w „Using extended runtime sessions” przedstawia je jako miejsce, w którym należy „dokończyć i posprzątać wszelkie zadania przed zakończeniem sesji.”3
  • Opuszczenie nadgarstka bez aktywnej sesji rozszerzonej wstrzymuje zegar. Opuszczenie nadgarstka z aktywną sesją rozszerzoną kontynuuje zegar. Sesja jest różnicą między „wdrożonym produktem” a „zepsutym przy drugim użyciu”.

Problem działania w tle, którego watchOS nie rozwiązuje na sposób iOS

Aplikacje iOS sięgają po kilka mechanizmów działania w tle, gdy potrzebują, aby aplikacja kontynuowała pracę przy wyłączonym ekranie:2

  • AVAudioSession z kategorią .playback utrzymuje aplikację audio przy życiu podczas odtwarzania muzyki.
  • Aktualizacje w tle CLLocationManager utrzymują aplikację nawigacyjną przy życiu z niebieskim paskiem.
  • BGTaskScheduler kolejkuje krótkie prace konserwacyjne, które system planuje według własnego harmonogramu.
  • Rozszerzenie UI pierwszego planu (Live Activity, CallKit, PushKit) łączy proces aplikacji z powierzchnią renderowania kontrolowaną przez system.

Żaden z nich nie pomaga na watchOS w sposób, w jaki można by zakładać. Aplikacje Watch nie mają tego samego mechanizmu planowania zadań w tle. Nie mają trybu AVAudioSession.playback w tle, który utrzymywałby zegar odliczający w ciszy. Mają jeden strukturalny prymityw dla „chcę nadal działać po tym, jak użytkownik opuści nadgarstek”, a tym prymitywem jest WKExtendedRuntimeSession z zadeklarowanym typem sesji.3

Typy sesji, które Apple obsługuje dla WKExtendedRuntimeSession, są celowo wąskie:3

  • self-care (krótkie aktywności wellness, środowisko pierwszoplanowe, limit 10 minut)
  • mindfulness (cicha medytacja, środowisko pierwszoplanowe, limit 1 godziny)
  • physical-therapy (rozciąganie i zakres ruchu, środowisko w tle, limit 1 godziny)
  • alarm (inteligentne alarmy, środowisko w tle, limit 30 minut, możliwość zaplanowania do 36 godzin naprzód przez start(at:))

Aplikacje treningowe używają HKWorkoutSession z odrębnym trybem w tle workout-processing; ta ścieżka jest udokumentowana dla rzeczywistych treningów i nie jest typem WKExtendedRuntimeSession.4 Tryb w tle underwater-depth obsługuje aplikacje do nurkowania i śledzenia głębokości za pośrednictwem ścieżki powierzchni sesji nurkowania, również nie przez WKExtendedRuntimeSession. Aplikacja może łączyć workout-processing z jednym typem sesji rozszerzonego środowiska uruchomieniowego, ale nie może wybrać więcej niż jednego typu rozszerzonego środowiska uruchomieniowego na aplikację.4

Aplikacje, które nie pasują do żadnej z tych kategorii, nie mogą używać WKExtendedRuntimeSession do działania po opuszczeniu nadgarstka. Aplikacje audio sięgają po kategorię sesji audio AVAudioSession.Category.playback oraz integrację Now Playing inną ścieżką kodu; aplikacje nawigacyjne używają aktualizacji w tle CLLocationManager. Watch nie jest komputerem ogólnego przeznaczenia; to urządzenie z ograniczeniami baterii, które model środowiska uruchomieniowego egzekwuje.

Zegar medytacji pasuje do mindfulness. Kontrakt: zadeklarować tryb w tle w pliku Info.plist, zażądać WKExtendedRuntimeSession, obsłużyć wywołania zwrotne delegata, zakończyć sesję po zakończeniu zegara. Apple dokumentuje limit sesji uważności jako jedną godzinę, z dyskrecją systemu w celu skrócenia tego czasu pod presją termiczną lub bateryjną.3

Wzorzec, który Return wdraża

Wzorzec zaczyna się od deklaracji w pliku Info.plist:4

<key>WKBackgroundModes</key>
<array>
    <string>mindfulness</string>
</array>

To deklaracja trybu sprawia, że typ sesji jest prawidłowy. Bez niej wywołanie WKExtendedRuntimeSession().start() kończy się niepowodzeniem po cichu, a aplikacja zostaje zawieszona po opuszczeniu nadgarstka tak samo, jak aplikacja Watch bez żadnego trybu w tle.

Sam menedżer sesji musi mieć zasięg aplikacji. Cykl życia widoku w SwiftUI jest nieprzyjazny dla długo żyjących obiektów stanowych: @StateObject i @State mają zasięg widoku, który je posiada, a wypchnięcie nawigacji, które zastępuje widok, znosi wraz z nim stan. WKExtendedRuntimeSession, którego delegat zostaje zdealokowany w trakcie sesji, nie powoduje awarii; sesja nadal działa, ale wywołania zwrotne delegata (willExpire, didInvalidateWith) docierają do zwolnionego obiektu, co oznacza, że czyszczenie nigdy się nie odbywa, co oznacza, że następne wywołanie startSession() myśli, że nie ma aktywnej sesji, i uruchamia duplikat.

Wdrożony wzorzec to singleton o zasięgu aplikacji. Poniższy fragment przedstawia kształt strukturalny; produkcja dodaje logowanie wewnątrz każdej metody dla obserwowalności:

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
    }
}

Trzy szczegóły strukturalne mają znaczenie wykraczające poza zgodność z protokołem.

Instancja static let shared jest zatrzymywana przez cały okres życia procesu aplikacji watch poprzez statyczne przechowywanie; ARC jej nie zdealokuje. To, co kupuje powiązanie na poziomie App, to nie dodatkowe zatrzymanie, lecz stabilny punkt obserwacji. Wzorzec błędu, który to zapobiega: menedżer sesji utrzymywany tylko przez tymczasowy widok, który zostaje zdjęty w trakcie sesji, gdzie widok umiera, ale static let shared przeżywa, z efektem ubocznym polegającym na tym, że każdy menedżer opakowany w @StateObject traci cykl obserwacji i przestaje poprawnie się przerenderowywać. Należy używać singletona plus akcesora @Observable na poziomie App, aby UI nadal obserwowało kanoniczną instancję.

Właściwość session jest zabezpieczeniem przed duplikatami sesji. Zegar z przyciskiem „zacznij od nowa” może wywołać startSession() z wielu ścieżek; sprawdzenie guard session == nil jest blokadą. Dwie współbieżne sesje rozszerzone powodują nieprzewidywalne zachowanie: czasem druga się powiedzie, a pierwsza staje się osierocona, czasem wywołanie startu kończy się po cichu niepowodzeniem. Inwariant pojedynczej sesji zapobiega całej tej klasie błędów.

Wywołania zwrotne delegata logują, ale rzadko działają. Wywołanie zwrotne didStart odpala się raz na sesję i jest użytecznym haczykiem dla obserwowalności; wywołanie zwrotne willExpire odpala się przed wymuszonym unieważnieniem przez system i jest miejscem, w którym przykład Apple oczekuje od aplikacji „dokończenia i posprzątania wszelkich zadań przed zakończeniem sesji”; wywołanie zwrotne didInvalidateWith jest miejscem, w którym referencja sesji zostaje wyczyszczona, aby następne wywołanie startSession() zadziałało. Wdrożony wzorzec to wywołania zwrotne aktualizują stan, automat stanu wykonuje pracę, a nie wywołania zwrotne wykonują pracę bezpośrednio.

Menedżer zegara wywołuje menedżera sesji przy każdym przejściu, które zmienia, czy zegar aktywnie odlicza:

@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
    }
}

Sesja kończy się przy pauzie, przy resecie i przy zakończeniu ostatniego cyklu. Uzasadnienie produktowe: wstrzymana medytacja nie musi nadal rezerwować budżetu środowiska uruchomieniowego, który system przyznaje pod mindfulness; wznowienie z pauzy ponownie pozyskuje świeżą sesję. Koszt produktowy polega na tym, że pauzy z opuszczonym nadgarstkiem nie można wznowić samym podniesieniem nadgarstka; użytkownik musi przywrócić aplikację na pierwszy plan, aby wznowić. Zysk produktowy polega na tym, że koszt baterii dla wstrzymanego zegara spada do zera, a system nie widzi przestarzałej sesji.

Opuszczenie nadgarstka jest testem

Testowanie watchOS w symulatorze to uprzejma fikcja. Symulator nie egzekwuje modelu środowiska uruchomieniowego opuszczonego nadgarstka tak, jak robi to prawdziwy Apple Watch. Symulator utrzymuje aplikację na pierwszym planie tak długo, jak okno symulatora ma fokus; sesja rozszerzonego środowiska uruchomieniowego w symulatorze wygląda identycznie jak brak sesji, ponieważ aplikacja pierwszego planu działa tak czy inaczej.

Rzeczywisty test odbywa się na prawdziwym Apple Watch:5

  1. Uruchomić zegar.
  2. Opuścić nadgarstek (lub nacisnąć przycisk boczny, aby zablokować ekran).
  3. Odczekać 30 sekund.
  4. Podnieść nadgarstek z powrotem.

Bez aktywnej sesji rozszerzonego środowiska uruchomieniowego aplikacja watch zostaje zawieszona; stan zegara jest zamrożony w momencie opuszczenia nadgarstka i wznawia się z tego zamrożonego stanu. W przypadku 5-minutowej medytacji, która wymaga od użytkownika zamknięcia oczu, błąd jest niewidoczny, dopóki zegar nie pomyli się o tyle, ile czasu oczy były zamknięte.

Z aktywną sesją rozszerzonego środowiska uruchomieniowego zegar nadal odlicza. Podniesienie nadgarstka ujawnia zegar w prawidłowej upływającej pozycji. Sygnał audio (jeśli zegar odtwarza go po zakończeniu) odpala się o prawidłowym czasie zegarowym, a nie o czasie podniesienia nadgarstka.

Scenariusz opuszczenia nadgarstka był błędem, z którym pierwsza wersja Watch w Return została wdrożona, i naprawiła go refaktoryzacja singletona. Naprawa to powyższy wzorzec singletona; błąd polegał na tym, że instancja WatchSessionManager była utrzymywana przez widok SwiftUI, który został zdealokowany przy wypchnięciu nawigacji. Sesja technicznie działała po stronie systemu, ale delegat został zwolniony; następne wywołanie startu sesji było po cichu pustą operacją, ponieważ właściwość session menedżera została ustawiona na obiekcie, który już nie żył. Testowanie na prawdziwym urządzeniu ujawnia awarię w sekundach. Testowanie w symulatorze nie ujawnia jej nigdy.

Co właściwie mówią wywołania zwrotne delegata

WKExtendedRuntimeSessionInvalidationReason wylicza sposoby, w jakie kończy się sesja:6

Powód Kiedy występuje
none Sesja została wyraźnie unieważniona przez aplikację wywołującą invalidate()
sessionInProgress Sesja tego samego typu już działa
expired Osiągnięto limit czasu narzucony przez system
resignedFrontmost Inna aplikacja stała się pierwszoplanowa, podczas gdy sesja działała
suppressedBySystem System stłumił sesję (niski poziom baterii, presja termiczna)
error Wystąpił nieusuwalny błąd; należy sprawdzić parametr error

Powody, które mają znaczenie dla projektowania produktu:

expired oznacza, że osiągnięto limit czasu narzucony przez system. Apple dokumentuje limit sesji uważności jako jedną godzinę;3 najdłuższy czas trwania medytacji w Return to 60 minut, co jest udokumentowanym pułapem. 90-minutowa medytacja nie może zostać ukończona w ramach pojedynczej sesji uważności: zegar umarłby w połowie sesji, w momencie godzinnym. Decyzja produktowa polega na ograniczeniu dostępnych czasów trwania do tego, co model środowiska uruchomieniowego jest udokumentowany dostarczyć, a nie na obstawianiu tolerancji systemu.

resignedFrontmost oznacza, że użytkownik otworzył inną aplikację Watch, a Twoja sesja przegrała. Użytkownicy Watch chętnie przeciągają do innej aplikacji, a potem zapominają. Decyzja produktowa polega na tym, by albo wstrzymać przy rezygnacji (stan zachowany, użytkownik może wrócić), albo zakończyć przy rezygnacji (sesja skończona, użytkownik dostaje sygnał „przerwałeś wcześnie”). Return wybiera wstrzymanie przy rezygnacji, dzięki czemu użytkownik może odebrać telefon w trakcie medytacji i wrócić.

suppressedBySystem to uprzejma wersja „zegarek jest gorący”. Urządzenie watchOS pod presją termiczną lub przy niskim poziomie baterii może odebrać sesję rozszerzonego środowiska uruchomieniowego nawet bez nadużycia ze strony aplikacji. Menedżer sesji musi obsłużyć ten przypadek z gracją: wyczyścić referencję, wyświetlić nieblokujące ostrzeżenie i nie wchodzić w stan, w którym próbuje zrestartować sesję, której system właśnie odmówił.

Wywołanie zwrotne willExpire odpala się, gdy sesja ma wygasnąć; przykład Apple przedstawia je jako moment, w którym należy „dokończyć i posprzątać wszelkie zadania przed zakończeniem sesji.”3 Wywołanie zwrotne jest miejscem, w którym aplikacja może zapisać końcowy zrzut stanu, odtworzyć kończący sygnał audio lub przedstawić UI „sesja kończy się wkrótce”. Return obecnie tylko loguje to wywołanie zwrotne; bogatsze czyszczenie (wpis dziennika HealthKit, wyciszanie audio) odbywa się na ścieżkach resetu i ukończenia zegara i jest na liście co zbudowałbym inaczej dla okna willExpire.

Co zbudowałbym inaczej

Dwie rzeczy, gdyby Return zaczynało od zera.

Używać HKWorkoutSession dla każdej sesji, której wartość rośnie wraz z integracją z HealthKit. Zegar medytacji znajduje się na granicy między mindfulness a workout-processing. Mindfulness był właściwym wyborem dla v1, ponieważ model danych jest prostszy, a oczekiwanie użytkownika to „to jest medytacja, a nie trening”. HKWorkoutSession niesie bardziej szczegółową integrację z HealthKit (start sesji, koniec sesji, segmenty, zdarzenia) i daje bogatszy interfejs LiveWorkoutBuilder do gromadzenia danych. Ocena architektoniczna, a nie udokumentowana gwarancja Apple: dla aplikacji, której wartość zależy od szczegółowej telemetrii sesji, ścieżka sesji treningowej obsługuje strukturę, której WKExtendedRuntimeSession nie obsługuje.

Dodać powierzchnię obserwowalności stanu sesji od pierwszego dnia. Pierwsza wersja Return logowała zdarzenia sesji do konsoli. Druga wersja dodała widoczność stanu sesji na urządzeniu do debugowania. Trzecia wystawiłaby przełącznik trybu deweloperskiego, który ujawnia użytkownikowi historię powodów sesji, gdy coś pójdzie nie tak, zamiast traktować unieważnienie sesji jako czarną skrzynkę. Środowisko uruchomieniowe watchOS jest nieprzejrzyste; powierzchnia debugowania musi to skompensować.

Kiedy WKExtendedRuntimeSession jest złą odpowiedzią

Trzy przypadki, w których typ sesji nie pasuje:

Treningi, które potrzebują znaczników segmentów, strumieni tętna lub aktywnego śledzenia kalorii. Należy używać HKWorkoutSession bezpośrednio z HKLiveWorkoutBuilder. Powierzchnia Workout to udokumentowana przez Apple ścieżka dla rzeczywistych treningów (i medytacji w ruchu lub intensywnej aktywności); WKExtendedRuntimeSession to udokumentowana ścieżka dla sesji niebędących treningami, takich jak uważność lub alarmy. Aplikacja medytacyjna nie potrzebuje treningu; aplikacja Couch-to-5K potrzebuje.

Odtwarzanie audio, które potrzebuje powierzchni Now Playing. Należy używać AVAudioSession skonfigurowanej do odtwarzania wraz z uprawnieniami sesji audio watchOS; integracja Now Playing plus systemowa powierzchnia odtwarzania to to, czego chcą aplikacje audio, a ścieżka audio jest całkowicie odrębna od WKExtendedRuntimeSession. WKExtendedRuntimeSession nie zapewnia Now Playing ani systemowego routingu audio.

Długo trwająca synchronizacja danych bez świadomości użytkownika. Należy używać WKApplicationRefreshBackgroundTask dla okresowych okien odświeżania, które system planuje. Użytkownik nie jest w aplikacji; aplikacja nie musi nadal działać; musi się krótko obudzić i odświeżyć. Dwa modele — zadania w tle i sesji rozszerzonego środowiska uruchomieniowego — służą bardzo różnym potrzebom.

Co wzorzec oznacza dla aplikacji wdrażanych na watchOS 11+

Trzy wnioski.

  1. Model środowiska uruchomieniowego Watch jest opt-in. Wybierz typ sesji i żyj wewnątrz jego reguł. Aplikacje, które próbują wykonywać „ogólną pracę w tle” na watchOS, przegrają. Wybierz mindfulness, workout-processing, self-care, physical-therapy, alarm lub underwater-depth i zaprojektuj doświadczenie użytkownika wokół budżetu środowiska uruchomieniowego, który wiąże się z wybranym typem sesji.

  2. Delegat sesji musi mieć zasięg aplikacji. Cykl życia widoku w SwiftUI nie chroni długo żyjących obiektów stanowych. Singleton static let shared powiązany na poziomie @main App jest najmniejszym wzorcem, który przeżywa wypchnięcia nawigacji, zastąpienia widoków i normalne zachowanie dealokacji w SwiftUI.

  3. Testować na prawdziwym sprzęcie. Symulator nie egzekwuje modelu środowiska uruchomieniowego opuszczonego nadgarstka. Błąd, którego aplikacja Watch nie może przetestować w symulatorze, to błąd, który dostarcza użytkownikom.

Ten wpis warto zestawić z moimi wcześniejszymi tekstami o tej samej rodzinie aplikacji: wieloplatformowe wdrożenie SwiftUI (Return wdraża się na iPhonie, iPadzie, Watch, Macu i Apple TV); automat stanu Live Activities (powierzchnia po stronie iOS dla tego samego zegara); wzorce HealthKit (gdzie sesje uważności Watch lądują w danych Health użytkownika). Pełen zestaw znajduje się w hubie serii Apple Ecosystem. Szerszy kontekst iOS-z-agentami-AI znaleźć można w przewodniku iOS Agent Development.

FAQ

Czym jest sesja rozszerzonego środowiska uruchomieniowego watchOS?

Sesja rozszerzonego środowiska uruchomieniowego watchOS (WKExtendedRuntimeSession) jest powierzchnią, której aplikacja Watch używa, aby kontynuować działanie po opuszczeniu nadgarstka przez użytkownika. Sesja musi zadeklarować typ (mindfulness, workout-processing, alarm itd.) za pomocą WKBackgroundModes w pliku Info.plist. Bez aktywnej sesji rozszerzonej watchOS zawiesza aplikację wkrótce po opuszczeniu nadgarstka.

Dlaczego mój zegar watchOS przestaje odliczać, gdy użytkownik opuszcza nadgarstek?

Aplikacja Watch zostaje zawieszona wkrótce po opuszczeniu nadgarstka, chyba że działa aktywna WKExtendedRuntimeSession obsługiwanego typu. Menedżer zegara, który nie uruchamia takiej sesji, zobaczy, że jego środowisko uruchomieniowe w tle zostaje odcięte, a stan zegara zamarza w momencie opuszczenia nadgarstka, dopóki użytkownik ponownie nie podniesie nadgarstka.

Jaka jest różnica między WKExtendedRuntimeSession a HKWorkoutSession?

WKExtendedRuntimeSession to uniwersalna powierzchnia rozszerzonego środowiska uruchomieniowego dla sesji niebędących treningami, takich jak mindfulness, alarm czy self-care. HKWorkoutSession to powierzchnia dla rzeczywistych treningów; integruje się z HealthKit, obsługuje znaczniki segmentów i jest udokumentowaną ścieżką dla medytacji w ruchu lub intensywnej aktywności. Aplikacje uważności bez telemetrii klasy treningowej używają pierwszej; aplikacje treningowe używają drugiej.

Czy system może odebrać moją sesję rozszerzonego środowiska uruchomieniowego?

Tak. WKExtendedRuntimeSessionInvalidationReason zawiera expired (osiągnięto systemowy limit czasu), resignedFrontmost (inna aplikacja Watch stała się pierwszoplanowa) oraz suppressedBySystem (niski poziom baterii lub presja termiczna). Menedżer sesji musi obsłużyć każdy z nich w sposób uporządkowany: referencja zostaje wyczyszczona, stan zegara reaguje odpowiednio, a następne wywołanie startu sesji działa poprawnie.

Gdzie powinien znajdować się menedżer sesji w aplikacji watchOS opartej na SwiftUI?

W zasięgu aplikacji, jako singleton powiązany ze struktury @main App. Stan o zasięgu widoku w SwiftUI (@State, @StateObject) zostaje zdealokowany przy wypchnięciach nawigacji, zastąpieniach widoków lub przejściu aplikacji w tło. Delegat sesji należący do widoku, który zostaje zwolniony w trakcie sesji, powoduje wyciek referencji sesji i uniemożliwia poprawne uruchamianie kolejnych sesji.

Bibliografia


  1. Autora Return, zegar medytacji w SwiftUI opublikowany w App Store 21 kwietnia 2026, dostępny na iPhone, iPad, Mac, Apple Watch i Apple TV. Aplikacja Watch używa WKExtendedRuntimeSession z trybem w tle mindfulness dla środowiska uruchomieniowego zegara cyklicznego. 

  2. Apple Developer, “About the background execution sequence”. Mechanizmy środowiska uruchomieniowego w tle po stronie iOS (sesje audio, lokalizacja, BGTaskScheduler) i jak różnią się od watchOS. 

  3. Apple Developer, “WKExtendedRuntimeSession”. Typy sesji, cykl życia, wywołania zwrotne delegata, limity środowiska uruchomieniowego oraz klucz WKBackgroundModes w pliku Info.plist. 

  4. Apple Developer, “Information Property List: WKBackgroundModes”. Obsługiwane łańcuchy typów sesji: workout-processing, mindfulness, self-care, physical-therapy, alarm, underwater-depth

  5. Apple Developer, “Building a watchOS app” oraz wskazówki dotyczące testowania WatchKit. Zachowania środowiska uruchomieniowego prawdziwego urządzenia nie da się odtworzyć w symulatorze watchOS; symulator nie egzekwuje zawieszenia przy opuszczeniu nadgarstka. 

  6. Apple Developer, “WKExtendedRuntimeSessionInvalidationReason”. Przypadki wyliczenia: none, sessionInProgress, expired, resignedFrontmost, suppressedBySystem, error

Powiązane artykuły

HealthKit + SwiftUI w iOS 26: autoryzacja, typy próbek i wzorce wieloplatformowe na podstawie dwóch wdrożonych aplikacji

Rzeczywiste wzorce produkcyjne z aplikacji Water (śledzenie nawodnienia, HKQuantitySample) oraz Return (sesje uważności,…

13 min czytania

Z czego zbudowane jest SwiftUI

SwiftUI to DSL oparte na result builderach, zbudowane na drzewie widoków typu wartościowego. Gdy podłoże staje się widoc…

13 min czytania

Warstwa porządkowa to prawdziwy rynek agentów AI

Charlie Labs zmieniło kierunek z budowania agentów na sprzątanie po nich. Rynek agentów AI przesuwa się z generowania w …

11 min czytania