← Wszystkie wpisy

Co zespół Swift z Apple powiedział podczas labu na WWDC26

Apple opublikowało WWDC26 Swift Group Lab jako godzinną, niepisaną sesję pytań i odpowiedzi z czterema inżynierami z zespołów Swift, Foundation oraz sieci po stronie serwera, a następnie udostępniło ją bez napisów.1 Przepuściliśmy nagranie przez lokalną transkrypcję, aby przeczytać, co naprawdę powiedzieli. Dopracowane sesje wyjaśniają, co robi dana funkcja. Lab to z kolei miejsce, w którym Holly przyznaje, że myliła się co do kluczowej decyzji dotyczącej współbieżności, w którym Corey tłumaczy wzorzec sprzątania (cleanup), który po cichu zawodzi w anulowanym kodzie, oraz w którym panel raz po raz powtarza radę, jakiej marketing nigdy nie poda: należy przestać kolekcjonować funkcje języka. Szczera wersja znajduje się właśnie tutaj.

Uwaga o źródłach: Apple nie opublikowało żadnej oficjalnej transkrypcji labu. Każdy poniższy cytat jest parafrazą lokalnej transkrypcji opublikowanego nagrania, z przybliżonymi znacznikami czasu. Osoby wypowiadające się oznaczono imieniem i zespołem, tak jak podaje je transkrypcja: Holly (zespół języka Swift, typy generyczne i współbieżność), Corey (zespół sieci po stronie serwera Swift), Tony (Foundation oraz biblioteka standardowa) i Doug (zespół języka Swift).1

TL;DR

  • Holly powiedziała, że gdyby zespół projektował współbieżność Swift dzisiaj, funkcje async nonisolated od pierwszego dnia działałyby w kontekście wywołującego. Przez „bardzo długi czas” wyznawała przeciwne przekonanie i zmieniła zdanie pod wpływem dowodów z rzeczywistych projektów. Swift 6.2 uczynił kontekst wywołującego standardowym zachowaniem domyślnym.1
  • Powtarzana wskazówka: warto celowo czynić swoje typy nie-Sendable. ~Sendable (SE-0518, zaimplementowane w Swift 6.4) daje czytelniejszy zapis, a Foundation na nowo adnotuje typy, które wcześniej oznaczono jako wyłączone z użytku — przy czym globalna instancja UserDefaults zyskuje zgodność z Sendable.21
  • Wzorzec asynchronicznego sprzątania według Coreya: anulowane konteksty po cichu pomijają asynchroniczne sprzątanie, dlatego należy przejrzeć każde async defer pod kątem zawieszających wywołań await i opakować je tarczą anulowania (cancellation shield). SE-0493 (defer async) i SE-0504 (withTaskCancellationShield) trafiają oba do Swift 6.4.34
  • Szczerość co do mapy drogowej: typ Disconnected to aktywna propozycja na forum, zgodności krotek (tuple conformances) są „prawie gotowe”, lecz formalnie wróciły do rewizji, UniqueArray przyjęto co do zasady, Subprocess 1.0 jest wydawany, a SwiftPM ujednolica się wokół Swift Build.5678
  • Powracający motyw panelu: „funkcje języka to nie przedmioty kolekcjonerskie”. Najpierw należy profilować, przyjąć wąskie narzędzie wskazane przez profiler, a resztę zostawić w spokoju.1

Decyzja dotycząca współbieżności, którą Apple podjęłoby inaczej

Obejrzyj: Swift Group Lab (WWDC26)

Retrospektywa Holly na temat współbieżności zaczyna się około 33:42. Cały lab nie ma oficjalnych napisów; poniższa transkrypcja pochodzi z lokalnego ASR.

Pewien deweloper zadał panelowi pytanie, do jakiego dopracowane sesje rzadko zachęcają: skoro współbieżność Swift 6 jest już w terenie dostatecznie długo, by obserwować jej adopcję, czy jest coś, co zespół zaprojektowałby dzisiaj inaczej? Holly odpowiedziała bez ogródek. Tak, z pewnością.

Konkretny żal dotyczy tego, gdzie wykonuje się funkcja async nonisolated. Dwie propozycje Swift Evolution przesunęły to zachowanie w przeciwnych kierunkach. Pierwsza sprawiała, że funkcje async nonisolated zawsze przeskakiwały do globalnej, współbieżnej puli wątków. Późniejsza, w Swift 6.2, kazała im pozostać w kontekście, który je wywołał, tak że funkcja wywołana z main actora pozostaje na main actorze.1 Holly powiązała ten zwrot z dowodami pochodzącymi od osób, które uruchamiały wczesne flagi sprawdzania współbieżności w prawdziwych aplikacjach i bibliotekach: przekazywały one typy nie-Sendable tam i z powrotem między kontekstem izolowanym przez aktora a tymi funkcjami async nonisolated, a domyślne zachowanie globalnej puli powodowało błędy wyścigu danych, ponieważ pierwotny aktor wciąż trzymał te wartości, gdy funkcja async wykonywała się gdzie indziej.1

Następnie pojawia się część, której nie ma w żadnym nagraniu sesji. „Przez bardzo długi czas trzymałam się przekonania, że wykonywanie na globalnej, współbieżnej puli wątków to właściwy model ze względu na długofalowe korzyści — powiedziała Holly — ale z czasem przekonały mnie problemy w rzeczywistych projektach” (parafraza ASR, ~35:50).1 Doug przedstawił pierwotną decyzję jako możliwy do obrony zakład w skali całego systemu: więcej dostępnej współbieżności oznacza więcej potencjalnej równoległości, co może oznaczać lepszą wydajność. Koszt ujawnił się dopiero, gdy ludzie zaczęli korzystać z pełnego modelu bezpieczeństwa, i spychał zbyt wiele typów ku Sendable, co Doug nazwał „nienaturalnym sposobem wyrażenia tych wszystkich idei”.1 Kontekst wywołującego jako domyślny jest bardziej przystępny, ponieważ zachowuje się jak kod niewspółbieżny, dopóki jawnie nie wybierze się równoległości. Jeśli ktoś przeszedł na Swift 6.2 i zastanawiał się, dlaczego historia współbieżności nagle stała się spokojniejsza, odpowiedź tkwi w tym, że osoby, które ją zaprojektowały, przyznały, iż pierwsza wartość domyślna była błędna, i dostarczyły poprawkę.

Warto celowo czynić swoje typy nie-Sendable

Najbardziej sprzeczna z intuicją rada z labu kłóci się z dwoma latami nawyków migracyjnych. Holly powiedziała, że nieustannie spotyka deweloperów pytających, jak uczynić swoje typy Sendable, podczas gdy lepsze pytanie często brzmi odwrotnie: czy ten typ powinien być nie-Sendable?1 W przypadku efemerycznych typów używanych w obrębie jednego obliczenia jawne oznaczenie ich jako nie-Sendable daje czystszy model danych i wyraźniejszą granicę między tym, co zamierza się przekazywać przez granice izolacji, a tym, czego się nie zamierza. Pomaga to również wydajności, ponieważ koszt przekazywania typu to koszt, którego nie chce się ponosić na wartościach pośrednich, które nigdy nie musiały podróżować.1

Swift 6.4 nadaje tej intencji realny zapis. ~Sendable (SE-0518) pozwala stłumić zgodność bezpośrednio, podobnie jak ~Copyable tłumi kopiowalność, zamiast pisać niedostępną zgodność.2 Holly precyzyjnie nakreśliła różnicę: niedostępna zgodność Sendable deklaruje, że dany typ i każda jego podklasa na pewno nie są sendable, i propaguje to w dół całej hierarchii, podczas gdy ~Sendable to jedynie brak zgodności, więc podklasa, która nie dodaje żadnego stanu mutowalnego, wciąż może dodać własną zgodność Sendable.21

Właśnie tej elastyczności potrzebowało Foundation. Tony wyjaśnił trzecią kategorię, która wyłoniła się z pierwotnego audytu Sendable w Foundation: klasy, w których nadklasa jest bezpieczna, ale podklasa może nie być. Jego przykładem była NSString, która jest niemutowalna i sendable, w przeciwieństwie do NSMutableString, będącej mutowalną podklasą, która sendable nie jest.1 Zamiast pospiesznie nadawać błędną adnotację, Foundation oznaczyło niejednoznaczne klasy jako nie-Sendable i poczekało. Teraz — powiedział Tony podczas labu — zespół używa adnotacji ze Swift 6.4, aby ponownie i dokładnie zaadnotować te typy, i spodziewa się, że standardowy, globalny UserDefaults zyska zgodność Sendable.1 Zgodność UserDefaults oraz szerszą reanotację Foundation należy traktować jako przypisane do labu: Tony podał oba te fakty w sesji pytań i odpowiedzi, a nie są to funkcje, które mogę niezależnie potwierdzić na podstawie opublikowanej propozycji. Stałe ostrzeżenie panelu obowiązuje również tutaj, ze strony Tony’ego: nie należy sięgać po @unchecked Sendable, by na siłę wymusić tryb Swift 6, ponieważ każda adnotacja unchecked odrzuca bezpieczeństwo, które kompilator mógłby dla nas udowodnić.1

Asynchroniczne sprzątanie wykonane tak, by faktycznie się wykonało

Corey odpowiada za najbardziej praktyczną korektę w labie, a jest to rodzaj błędu, który wypuszcza się, nie zauważając go. Sztandarowym zwycięstwem współbieżności strukturalnej jest automatyczna propagacja anulowania: anuluje się zadanie i wszystko, co powstało na jego rzecz, również zostaje anulowane — czego właśnie się oczekuje, gdy widok zostaje zamknięty albo klient się rozłącza.1 Pułapka tkwi w sprzątaniu. Można mieć plik zapisany w połowie, który trzeba zrzucić do znanego, poprawnego stanu, albo transakcję bazodanową do wycofania, a samo to sprzątanie jest asynchroniczne. Jak ujął to Corey, większość kodu Swift odmawia wykonania w anulowanym kontekście, ponieważ zakłada, że powinien jak najszybciej się zwinąć, więc asynchroniczne sprzątanie działające wewnątrz anulowanego zadania może po cichu nie wykonać swojego zadania.1

Poprawką jest tarcza anulowania i łączy się ona wprost z asynchronicznym defer. SE-0493 pozwala, by blok defer zawierał wywołania await, i trafia do Swift 6.4.3 SE-0504 dodaje withTaskCancellationShield, również w Swift 6.4, które uruchamia blok tak, jakby anulowanie nie zostało zażądane, dzięki czemu zawieszające sprzątanie dobiega końca.4 Recepta audytu Coreya jest konkretna: należy spojrzeć na każdy blok async defer i zapytać, czy któreś z await w jego wnętrzu rzeczywiście się zawiesi. Jeśli tak, trzeba je opakować tarczą anulowania.1 Nazwał te dwa elementy „znakomitym towarzystwem” oraz „naprawdę dobrym ciosem jeden-dwa dla porządnego sprzątania zasobów”.1 Typy nie-Sendable wzmacniają tę samą dyscyplinę, zauważył Corey, ponieważ wartość, która nie może przekraczać domen współbieżności, zmusza asynchroniczny przepływ sterowania do pozostania liniowym i łatwym do prześledzenia.1

Szerszą radę Coreya dotyczącą współbieżności strukturalnej domyka tę sekcję. Należy zdecydowanie się na nią opierać, ponieważ to furtki ucieczkowe sprawiają kłopoty. Niestrukturalnych zadań (Task.detached albo gołego Task) warto unikać „niemal za wszelką cenę”, chyba że celowo wysyła się pracę gdzie indziej, i nigdy nie w głównym przepływie. Grupy zadań (task groups) powinny być wyborem domyślnym, cykle życia obiektów należy dopasować do ich zakresu leksykalnego, a kod asynchroniczny pisać liniowo: recepta krok A, potem B, potem C, sięgając po równoległość wewnątrz grup zadań tylko wtedy, gdy praca naprawdę się rozgałęzia.1

Szczerość co do mapy drogowej

Lab to także miejsce, w którym panel oddziela funkcje już wydane od tych optymistycznych, a różnica ma znaczenie, gdy decyduje się, na czym budować.

Kwestia przechowywania przeniesionej wartości nie-Sendable ma w zanadrzu realną odpowiedź. Dziś przenosi się za pomocą słowa kluczowego sending, a izolacja oparta na regionach pozwala kompilatorowi udowodnić, że pierwotny właściciel zrzekł się dostępu, lecz jeśli taką wartość trzeba przechować, działają wyłącznie niebezpieczne wyłączenia.1 Holly wskazała na Disconnected — aktywną propozycję na forum dla typu, który zachowuje właściwość przeniesienia poprzez przechowywanie, tak by można było odłożyć wartość i przekazać ją później. Jest właśnie projektowana i omawiana, nie zaimplementowana.51

Zgodności krotek to najwyrazistszy przykład optymizmu inżyniera wobec formalnego statusu. Zapytana, co pozostaje, zanim krotki zaczną warunkowo spełniać Equatable, Hashable i Comparable, Holly wyjaśniła, że to rozwinięcie paczek parametrów (parameter packs): język potrzebuje składni, by zapisać rozszerzenie nad krotką, której typy elementów tworzą paczkę parametrów, z klauzulą where wymagającą zgodności od każdego elementu.1 Powiedziała, że eksperymentalna implementacja znajduje się w repozytorium kompilatora i jest „już prawie na samym końcu”.1 Zapis formalny jest ostrożniejszy. SE-0283, pierwotna propozycja zgodności krotek, została zwrócona do rewizji, a jej implementację z 2020 roku cofnięto; ogólne podejście oparte na paczkach parametrów istnieje dziś jedynie za eksperymentalną flagą kompilatora TupleConformances.10 „Prawie gotowe” z ust Holly należy odczytywać jako szczerą ocenę implementacji przez inżynierkę, a nie jako wydaną funkcję.

Kontenery wydajnościowe są dalej zaawansowane. UniqueArray, o którym wspomniał Tony, pochodzi z SE-0527 („RigidArray and UniqueArray”), obecnie przyjętego co do zasady, które wprowadza nowy moduł Containers w bibliotece standardowej; jest już dostarczany we wczesnej formie w swift-collections 1.3.7 Tony przedstawił go jako ulepszenie dla Array, którego ruch retain/release związany z kopiowaniem przy zapisie (copy-on-write) ujawnia się w śladzie Instruments.1 Subprocess — wieloplatformowe, otwartoźródłowe API procesów, które Tony wymienił jako swoje ulubione — wydaje w tym roku wersję 1.0; kamień milowy 1.0 jest zapowiedziany, a najnowszy opublikowany tag wciąż znajduje się na 0.5, jest więc raczej wydawany niż wydany.61 Po stronie narzędzi Holly opisała zmianę w SwiftPM, której deweloperzy mogą nie zauważyć: kompilacje pakietów w Xcode oraz kompilacje otwartoźródłowego łańcucha narzędzi dzielą teraz jedną implementację, Swift Build, która staje się domyślnym zapleczem systemu kompilacji (domyślnie na gałęzi main, z celem 6.4).81

Na koniec ścieżka migracji, od której zaczęła Holly: @diagnose, nowy atrybut z SE-0522 („Source-Level Control Over Compiler Warnings”), przyjęty.9 Daje kontrolę nad ostrzeżeniami na poziomie kodu źródłowego, dzięki czemu można stłumić ostrzeżenia o wycofaniu (deprecation) w jednym regionie albo włączyć domyślnie wyłączone ostrzeżenia, takie jak ścisłe bezpieczeństwo pamięci czy ścisła współbieżność, w obszarach, które są najważniejsze, co czyni go bardziej precyzyjnym narzędziem do etapowej migracji do Swift 6.1

Wskazówki inżynierów, zebrane razem

Wartość labu tkwi w nagromadzonych radach praktyków, które nigdy nie mieszczą się w czasie trwania sesji. Najważniejsze punkty, z przypisaniem:

  • Należy pisać liniowy kod asynchroniczny. Corey: niestrukturalnych zadań warto unikać niemal za wszelką cenę, każde zadanie utrzymywać jako sekwencyjną receptę, a równoległość wprowadzać tylko wewnątrz grup zadań, gdy praca naprawdę się rozgałęzia.1
  • Recepta na migrację do MainActor. Holly: warto zacząć od typów liściowych (leaf) i posuwać się na zewnątrz, albo włączyć main-actor-by-default dla modułu, który ma być w całości na main actorze, a jawnie zaadnotować części odciążone. Metody, które nie dotykają żadnego stanu mutowalnego, należy oznaczyć nonisolated, a static var, która faktycznie nigdy nie jest mutowana, zamienić na let, by mogła pozostać nonisolated.1
  • Koszty zgodności są nierówne. Doug: nieużywana zgodność Equatable lub Hashable zachowuje w pliku binarnym swój kod świadka (witness), ponieważ rzutowanie as? na egzystencjał w czasie wykonania może ją odkryć, co kosztuje rozmiar kodu; Sendable to czysta etykieta z czasu kompilacji bez reprezentacji w czasie wykonania, więc nieużywana zgodność Sendable jest darmowa.1
  • Połączenie @inlinable z @inline(never). Ulubiona sztuczka Coreya: @inlinable udostępnia ciało funkcji poza granicami modułu i odblokowuje specjalizację generyczną oraz propagację efektów, podczas gdy @inline(never) powstrzymuje wbudowanie (inline) zimnej, obciążonej kodem ścieżki (bajtowej kopii zapasowej), tak by gorąca ścieżka pozostała szybka. Użył tej pary „trzy razy w całym portfolio sieciowym Swift”, w nietypowych problemach wydajnościowych dotyczących zimnej ścieżki.1
  • Należy optymalizować najgorętszą ścieżkę, a nie cały projekt. Corey: „Odnieśliśmy wielki sukces, wprowadzając spans i kilka unique arrays właśnie wzdłuż najgorętszej ścieżki”, co dostarcza niemal wszystkich zysków wydajnościowych przy niewielkiej, wspieranej przez kompilator zmianie. Nowe typy wydajnościowe warto trzymać w izolacji, aby ich przyjęcie nigdy nie wymuszało gigantycznej refaktoryzacji.1
  • Należy przestać kolekcjonować funkcje. Powracające zdanie panelu, zaczerpnięte od przyjaciela, którego zacytował Corey: „funkcje języka to nie przedmioty kolekcjonerskie”. Za użycie ich wszystkich nie ma nagrody. Najpierw należy profilować, sięgnąć po wąskie narzędzie wskazane przez profiler (typy niekopiowalne, UniqueArray, Span), a resztę godzin poświęcić na poprawki błędów i funkcje.1

FAQ

Czy Apple naprawdę opublikowało lab Swift z WWDC26 bez napisów?

Tak. Apple zamieściło Swift Group Lab (sesja 8001) jako nagranie bez oficjalnej transkrypcji ani napisów w witrynie dla deweloperów.1 Aby cytować go dokładnie, przepuściłem nagranie przez lokalną transkrypcję, więc każdy cytat w tym wpisie jest parafrazą tego lokalnego ASR, z przybliżonymi znacznikami czasu, przypisaną według imienia i zespołu.1

Co zespół Swift powiedział, że zmieniłby we współbieżności?

Holly powiedziała, że gdyby Apple projektowało współbieżność Swift dzisiaj, funkcje async nonisolated od początku działałyby w kontekście wywołującego, zamiast przeskakiwać do globalnej, współbieżnej puli wątków. Przez długi czas wyznawała przekonanie o globalnej puli i zmieniła zdanie pod wpływem dowodów z rzeczywistych projektów, a Swift 6.2 uczynił zachowanie kontekstu wywołującego domyślnym.1

Czy powinienem czynić swoje typy Swift Sendable, czy nie-Sendable?

Rada z labu brzmi: efemeryczne typy obliczeniowe warto celowo czynić nie-Sendable. Swift 6.4 dodaje do tego zapis ~Sendable (SE-0518), odmienny od niedostępnej zgodności, ponieważ podklasy wciąż mogą dodać Sendable, gdy nie dodają stanu mutowalnego.2 Tony powiedział, że Foundation na nowo adnotuje swoje niejednoznaczne klasy nową funkcją i spodziewa się, że globalny UserDefaults zyska zgodność z Sendable — obie te informacje są przypisane do labu.1

Jak upewnić się, że asynchroniczne sprzątanie wykona się, gdy zadanie zostanie anulowane?

Anulowane konteksty sprawiają, że większość kodu Swift pomija zawieszającą się pracę, więc asynchroniczne sprzątanie może po cichu zawieść. Wzorzec Coreya polega na przejrzeniu każdego async defer pod kątem wywołań await, które faktycznie się zawieszają, i opakowaniu ich tarczą anulowania. Asynchroniczne defer (SE-0493) oraz withTaskCancellationShield (SE-0504) trafiają oba do Swift 6.4.341

Czy zgodności krotek są wydawane w Swift?

Jeszcze nie. SE-0283 zwrócono do rewizji, a jej pierwotną implementację cofnięto; ogólne podejście oparte na paczkach parametrów istnieje wyłącznie za eksperymentalną flagą TupleConformances. Holly powiedziała podczas labu, że eksperymentalna implementacja w kompilatorze jest „już prawie na samym końcu”, co odzwierciedla optymizm inżynierki, a nie wydaną funkcję.1


Lab jest szczerym towarzyszem zapowiedzi: po wydane funkcje w kontekście sięgnij do nowości w Swift 2026, a po mechanikę migracji stojącą za domyślnym kontekstem wywołującego, o którym mówiła Holly, zajrzyj do współbieżności Swift 6.2 w praktyce. Filozofia panelu — „najpierw profiluj, przyjmij wąskie narzędzie” — rozciąga się na testowanie w Swift Testing kontra XCTest. Całość relacji z WWDC26 znajduje się w serii Apple Ecosystem.

Bibliografia


  1. Apple, WWDC 2026 session 8001, Swift Group Lab. Apple published no official transcript or captions for this lab; all quotes attributed to it are paraphrases from a local transcription of the published video, with approximate timestamps. Source for the concurrency retrospective (Holly, ~33:42), the make-types-non-Sendable guidance, the Foundation re-annotation and UserDefaults Sendable conformance (both lab-attributed to Tony), the async-cleanup and cancellation-shield pattern (Corey), the structured-concurrency and MainActor migration advice (Corey and Holly), the conformance-cost explanation (Doug), the @inlinable plus @inline(never) combo and hottest-path performance guidance (Corey), the Disconnected, tuple-conformance, UniqueArray, Subprocess, SwiftPM, and @diagnose remarks, and the “language features aren’t collectibles” theme. 

  2. Apple / Swift Evolution, SE-0518, ~Sendable for explicitly marking non-Sendable types. Status: Implemented (Swift 6.4). Source for suppressing the Sendable conformance and the distinction from an unavailable conformance. 

  3. Apple / Swift Evolution, SE-0493, Support async calls in defer bodies. Status: Implemented (Swift 6.4). Source for await inside defer blocks. 

  4. Apple / Swift Evolution, SE-0504, Task cancellation shields. Status: Implemented (Swift 6.4). Source for withTaskCancellationShield

  5. Swift Forums, Pitch: Disconnected type for modeling disconnected values. Active forums pitch for safely storing transferred non-Sendable values; not implemented. 

  6. Apple / Swift, Subprocess — cross-platform, open-source process API announced in 2025; version 1.0 announced as releasing this year, with the latest published tag at 0.5. Status per the lab and the project’s published releases. 

  7. Apple / Swift Evolution, SE-0527, RigidArray and UniqueArray. Status: Accepted in Principle; introduces a new standard-library Containers module and ships in an early form in swift-collections 1.3. 

  8. Swift Forums, SwiftPM development update: default build system change. Swift Build is becoming the default SwiftPM build-system backend (default on main, targeting 6.4). 

  9. Apple / Swift Evolution, SE-0522, Source-Level Control Over Compiler Warnings. Status: Accepted. Source for the @diagnose attribute and region-scoped warning control. 

  10. Apple / Swift Evolution, SE-0283, Tuples Conform to Equatable, Comparable, and Hashable. Status: Returned for revision; original implementation reverted. Source for the formal status of tuple conformances against the experimental TupleConformances flag. 

Powiązane artykuły

What's New in Swift (2026): The WWDC26 Update

Swift 6.3 and 6.4 from WWDC26: anyAppleOS availability, module selectors, borrow/mutate accessors, the Iterable protocol…

19 min czytania

Co zespół wydajności Apple powiedział podczas labu na WWDC26

Zespół Power & Performance Apple odpowiadał na żywo na pytania deweloperów podczas WWDC26. Praktyczne wskazówki dotycząc…

12 min czytania

Loop Engineering: Loops Win Where Verification Is Cheap

Loop engineering, checked against Boris Cherny's full transcripts: every loop he names has cheap verification. That cons…

19 min czytania