Z czego zbudowane jest SwiftUI
SwiftUI opiera się na trzech funkcjach języka Swift: result builderach, nieprzezroczystych typach zwracanych oraz drzewie widoków typu wartościowego. Gdy podłoże staje się widoczne, te elementy SwiftUI, które zaskakują programistów (AnyView, Group, ViewBuilder, parametry @ViewBuilder, osławiony błąd some View vs any View), przestają być tajemnicze.
Widok SwiftUI to typ wartościowy zgodny z jednym protokołem mającym jeden wymóg. Reszta frameworku jest zbudowana na funkcjach języka Swift istniejących poza SwiftUI: result builderach, typach nieprzezroczystych, generykach z ograniczeniami, property wrapperach. Jeśli rozumie się funkcje języka, framework czyta się jak normalny kod API w Swift. Jeśli nie — framework czyta się jak magia, która od czasu do czasu gryzie.
Niniejszy wpis omawia podłoże. Nie ma tu LiveActivityManager ani zrzutów ekranu z Get Bananas. Chodzi o framework, nie o projekt; gdy framework staje się czytelny, każdy wpis o wdrożonym kodzie w klastrze czyta się przejrzyściej.
TL;DR
- Widok SwiftUI to typ wartościowy Swift zgodny z
View. Protokół ma jeden wymóg:var body: some View { get }. Cała reszta zbudowana jest na funkcjach języka Swift. @ViewBuilderto result builder. Ciało każdegoViewnim jest. Result buildery zamieniają wyrażenia oddzielone przecinkami w pojedynczą wartość zwracaną poprzez wywołania syntetyzowane przez kompilator.some Viewto nieprzezroczysty typ zwracany. Kompilator zna typ konkretny; wywołujący nie. Typ nieprzezroczysty sprawia, że ciała widoków są szybkie zarówno w czasie kompilacji, jak i działania;AnyViewjest klapą bezpieczeństwa z wymazanym typem dla przypadków, w których nieprzezroczystość się nie sprawdza.Group,EmptyView,TupleView,_ConditionalContentto typy implementacyjne, które syntetyzują result buildery. Są udokumentowane, ale rzadko pisane ręcznie.
Protokół, od którego wszystko się zaczyna
Protokół View ma jeden wymóg:1
public protocol View {
associatedtype Body : View
@ViewBuilder var body: Self.Body { get }
}
Dwie części tego protokołu mają znaczenie dla zrozumienia reszty SwiftUI.
Typ powiązany Body : View. Ciało widoku samo jest widokiem. Ta rekurencja sprawia, że framework jest komponowalny. Każdy View zwraca kolejny View ze swojego ciała i tak dalej, aż łańcuch zakończy się na jednym z widoków pierwotnych frameworku (takich jak Text, Color, Image, EmptyView), których Body to Never. Widoki pierwotne to liście drzewa; widoki, które się pisze, to gałęzie.
Atrybut @ViewBuilder na body. Każde ciało jest domknięciem result buildera. Result buildery to funkcja języka Swift udokumentowana w SE-0289 (sformalizowana jako @resultBuilder w Swift 5.4), która pozwala domknięciu z sekwencją wyrażeń zostać przekształconym przez kompilator w pojedynczą wartość zwracaną poprzez syntetyzowane wywołania metod.2 To przekształcenie sprawia, że składnia bez przecinków, w kształcie instrukcji, wewnątrz ciała SwiftUI działa.
Kształt protokołu jest nietypowy z dwóch powodów.
Po pierwsze, wymóg jest właściwością obliczaną, nie metodą. Ciało widoku jest ponownie obliczane przy każdym przebiegu renderowania, gdy SwiftUI uznaje, że stan widoku uległ zmianie. Framework traktuje body jako tani w wywołaniu; długie obliczenia wewnątrz body to antywzorzec, ponieważ wykonują się przy każdym renderowaniu.
Po drugie, Self.Body jest powiązany, nie wymazany. Konkretny typ ciała widoku jest częścią jego sygnatury w czasie kompilacji. Typ ciała Text("Hello") to Never; typ ciała widoku własnego to to, co @ViewBuilder zsyntetyzował dla ciała. Konstrukcja oparta na typie powiązanym pozwala kompilatorowi optymalizować drzewo widoków bez sprawdzania typów w czasie wykonania. To również tworzy wymóg some View, gdy widok własny zwraca treść warunkową.
Result buildery: DSL bez przecinków
Result builder to funkcja języka Swift, która przekształca domknięcie w pojedynczą wartość zwracaną poprzez wstawienie wywołań metod syntetyzowanych przez kompilator. @ViewBuilder jest result builderem. Ciało każdego widoku SwiftUI jest jego domknięciem.2
Rozważmy ten widok:
struct ExampleView: View {
var body: some View {
Text("Title")
Text("Subtitle")
Image(systemName: "star")
}
}
Ciało zawiera trzy instrukcje bez separatora. W normalnym Swift to błąd kompilacji: domknięcie może zwrócić tylko jedną wartość. Result buildery przepisują domknięcie przed kompilacją. Faktyczny kod, który widzi kompilator, po rozszerzeniu @ViewBuilder, wygląda mniej więcej tak:
struct ExampleView: View {
var body: some View {
ViewBuilder.buildBlock(
Text("Title"),
Text("Subtitle"),
Image(systemName: "star")
)
}
}
ViewBuilder.buildBlock(_:_:_:) to metoda statyczna, która przyjmuje trzy widoki i zwraca TupleView<(Text, Text, Image)>. Ciało zwraca tę pojedynczą wartość typu tuple-view. Starsze SwiftUI dostarczało stały zestaw przeciążeń buildBlock dla 1, 2, 3, … aż do 10 dzieci; obecne SwiftUI używa wsparcia variadic generics w Swift (buildBlock<each Content>), więc ciało z jedenastoma lub większą liczbą widoków siostrzanych nie jest już przypadkiem szczególnym.
Ten sam wzorzec obsługuje sterowanie przepływem. Ciało widoku z instrukcją if wygląda tak:
struct ConditionalView: View {
let isActive: Bool
var body: some View {
if isActive {
Text("Active")
} else {
Text("Inactive")
}
}
}
@ViewBuilder przepisuje to poprzez wywołania buildEither(first:) / buildEither(second:), produkując _ConditionalContent<Text, Text>. Kompilator zna typ wynikowy w czasie kompilacji, mimo że tylko jedna gałąź wykonuje się przy danym renderowaniu.
if let, switch, rozpakowanie wartości opcjonalnych i kilka innych konstrukcji są obsługiwane przez różne statyczne metody buildXxx result buildera.3 Powtarzająca się treść to jeden istotny przypadek, który funkcja języka wspiera poprzez buildArray, ale @ViewBuilder już nie: surowa pętla for wewnątrz body zawodzi z komunikatem “closure containing control flow statement cannot be used with result builder ‘ViewBuilder’.” Rozwiązaniem w stylu SwiftUI jest ForEach, który przyjmuje RandomAccessCollection oraz domknięcie treści i syntetyzuje iterację jako pojedynczy widok typu wartościowego. DSL nie jest stworzone na zamówienie; to result buildery Swift, skonfigurowane dla widoków.
some View i problem nieprzezroczystości
Ciało widoku własnego zwykle zwraca some View. To słowo kluczowe to nieprzezroczysty typ zwracany i zostało dodane w Swift 5.1.4
some View mówi: „Zwracam określony typ zgodny z View, ale nie powiem, który.” Kompilator śledzi konkretny typ wewnętrznie dla optymalizacji; wywołujący widok widzi tylko świadka protokołu. Wzorzec ten pozwala ciału widoku zwrócić złożony typ, taki jak VStack<TupleView<(Text, Image, Spacer)>>, bez konieczności pisania tego typu w kodzie źródłowym.
Dwie rzeczy dotyczące some View, które dezorientują nowych programistów SwiftUI:
some View to jeden konkretny typ, nawet gdy zwraca się różne rzeczy. Wyrażenie if condition { Text("A") } else { Image("b") } jest dozwolone wewnątrz ciała @ViewBuilder, ponieważ result builder opakowuje obie gałęzie w _ConditionalContent, produkując pojedynczy konkretny typ. Ale wyrażenie if condition { return Text("A") } else { return Image("b") } poza result builderem to błąd kompilacji: dwie gałęzie zwracają różne konkretne typy, a some View wymaga jednego. Result buildery sprawiają, że warunkowe kształty zwracania działają; jawne return tracą transformację result buildera.
some View to nie to samo, co any View. some View jest nieprzezroczyste (jeden konkretny typ, ukryty); any View jest egzystencjalne (pudełko, które może pomieścić dowolny zgodny typ, z narzutem czasu wykonania). Wolna funkcja lub właściwość może legalnie zwrócić any View. Ciało body protokołu View jednak nie może: protokół wymaga associatedtype Body: View, a any View samo w sobie nie jest zgodne z View, więc var body: any View nie spełnia protokołu i kompilator sugeruje some View. Zasada praktyczna: należy używać some View dla ciał widoków, sięgnąć po AnyView (opakowanie z wymazanym typem) dla typów widoków zmiennych w czasie wykonania. Komunikat błędu “function declares an opaque return type but the return statements in its body do not have matching underlying types” prawie zawsze oznacza, że próbowano zwrócić różne konkretne typy z funkcji some View i potrzeba albo rozgałęzienia result buildera, albo AnyView.
AnyView: klapa bezpieczeństwa
AnyView to opakowanie widoku z wymazanym typem. Konstrukcja to AnyView(myView). Opakowanie zawiera dowolny zgodny widok, a SwiftUI akceptuje je tam, gdzie oczekiwany jest View.5
Przypadek użycia jako klapy bezpieczeństwa to funkcja, która zwraca różne konkretne typy w oparciu o dane czasu wykonania i której nie da się wyrazić poprzez rozgałęzienie result buildera:
func viewForKind(_ kind: Kind) -> AnyView {
switch kind {
case .text: return AnyView(Text("hello"))
case .image: return AnyView(Image("photo"))
case .custom: return AnyView(MyCustomView())
}
}
Kosztem AnyView jest to, że typ bazowy nie jest częścią statycznej tożsamości widoku. Dokumentacja Apple opisuje konsekwencję wprost: gdy typ zawinięty wewnątrz AnyView zmienia się między renderowaniami, istniejąca hierarchia widoków jest niszczona, a w jej miejsce tworzona nowa, co oznacza utracony stan, restartowane animacje i utraconą tożsamość. Ponowne opakowanie tego samego konkretnego typu nie wyzwala tego zniszczenia, ale różnicowanie sterowane statycznym typem, które framework preferuje, i tak nie jest już dostępne.
Właściwa zasada brzmi: warto preferować rozgałęzienie result buildera @ViewBuilder dla widoków warunkowych (if, switch, for), preferować widoki sparametryzowane dla zmiennych typów, sięgać po AnyView tylko wtedy, gdy żadne z tych nie działa. func viewForKind, która zwraca AnyView, zwykle jest sygnałem, że należy uczynić viewForKind zwracającą some View i umieścić switch wewnątrz domknięcia result buildera.
Group, EmptyView, TupleView: typy implementacyjne
Result builder syntetyzuje określone konkretne typy widoków. Trzy z nich warto rozpoznawać:6
Group to przezroczysty kontener. Akceptuje do dziesięciu widoków jako treść i prezentuje je jako rodzeństwo dla układu nadrzędnego. Sam kontener nie dodaje struktury wizualnej; treści renderują się dokładnie tak, jak by to robiły indywidualnie. Przypadek użycia to opakowanie wielu widoków w kontekście oczekującym pojedynczego widoku (modyfikator .if, warunkowe return, funkcja produkująca „jeden widok”). Group { Text("A"); Text("B") } to pojedynczy widok zawierający dwa; jest to jawna forma tego, co result buildery robią niejawnie.
EmptyView to widok, który nic nie renderuje. Result builder używa go jako warunkowej gałęzi false, gdy if nie ma else. Zwracanie EmptyView() z własnego kodu to sposób rezygnacji z renderowania bez zmieniania typu zwracanego funkcji.
TupleView to konkretny typ, który result buildery produkują, gdy ciało ma wiele widoków siostrzanych. Wyrażenie na początku tego wpisu, które zwraca trzy widoki siostrzane, faktycznie zwraca TupleView<(Text, Text, Image)>. Prawie nigdy nie pisze się TupleView bezpośrednio; czyta się go w komunikatach błędów.
_ConditionalContent (z wiodącym podkreślnikiem) to typ obsługujący gałęzie if/else. Typ pojawia się w publicznej powierzchni ViewBuilder, ale nazwa z podkreślnikiem sygnalizuje „nie pisać przeciwko temu beztrosko”; lepiej pozwolić result builderowi zsyntetyzować go z if/else, niż konstruować go ręcznie.
@ViewBuilder na własnych funkcjach
Result buildery nie są tylko dla body. Każda funkcja lub parametr domknięcia może zostać oznaczony @ViewBuilder, a wewnątrz niej staje się legalna ta sama składnia DSL.2
struct Card<Content: View>: View {
let content: Content
init(@ViewBuilder content: () -> Content) {
self.content = content()
}
var body: some View {
VStack(alignment: .leading, spacing: 8) {
content
}
.padding()
.background(.regularMaterial)
.clipShape(RoundedRectangle(cornerRadius: 12))
}
}
// Usage: callers get the result-builder DSL inside the closure.
Card {
Text("Title")
Text("Subtitle")
Image(systemName: "star")
}
W ten sposób własne VStack, HStack, ZStack, List, Form, Section, Group i NavigationStack w SwiftUI akceptują wiele dzieci. Każdy z tych typów przyjmuje parametr @ViewBuilder content: () -> Content. Rozpoznanie tego wzorca oznacza, że można pisać własne widoki kontenerowe z taką samą ergonomią jak te z frameworku, bez specjalnego wsparcia kompilatora.
Powodem, dla którego pisze się init(@ViewBuilder content:), a nie po prostu init(content:), jest to, że atrybut na parametrze aktywuje transformację result buildera wewnątrz ciała domknięcia, które przekazuje wywołujący. Bez tego atrybutu Card { Text("A"); Text("B") } to błąd kompilacji, ponieważ domknięcie ma dwie instrukcje i nie ma @ViewBuilder, który by je przekształcił.
Stan, powiązania i warstwa property wrapperów
Wszystko powyżej dotyczy kształtu drzewa widoków. Druga połowa SwiftUI to stan, a ta połowa zbudowana jest na property wrapperach Swift.7
Property wrappery najistotniejsze dla autora widoków:
@State posiada fragment stanu typu wartościowego wewnątrz pojedynczego widoku. Odczyt właściwości czyta podstawowy magazyn; przypisanie do niej wyzwala ponowne renderowanie widoku. Wrapper jest odpowiedni dla prostego, lokalnego stanu widoku (włącznik on/off, wersja robocza ciągu znaków pola tekstowego).
@Binding to dwukierunkowe odniesienie do stanu innego widoku. Widok podrzędny, który musi czytać i zapisywać stan rodzica, przyjmuje parametr Binding<T>. Rodzic konstruuje powiązanie poprzez $state (projekcja z dolarem na @State).
@Observable (iOS 17+) to makro zastępujące starszy wzorzec zgodności z ObservableObject. Makro zastosowane do klasy generuje śledzenie z frameworka Observation, dzięki czemu właściwości klasy wyzwalają ponowne renderowania widoków, gdy są czytane wewnątrz ciała, a później zmieniane. Własność po stronie widoku klasy @Observable przenosi się z @StateObject na zwykłe @State; widoki pochodne, które potrzebują dwukierunkowego uchwytu, używają @Bindable zamiast @ObservedObject.
@Environment czyta wartości wstrzyknięte poprzez zależności z łańcucha środowiska. SwiftUI dostarcza wbudowane klucze środowiska (locale, schemat kolorów, akcja zamykania); aplikacje dodają niestandardowe klucze do wstrzykiwania zależności specyficznego dla domeny.
Warstwa property wrapperów pozwala body widoku ponownie się wykonać, gdy stan się zmienia. SwiftUI śledzi odczyty wewnątrz body poprzez dwa odrębne mechanizmy: AttributeGraph (prywatny graf zależności Apple, który stoi za @State, @Binding i @Environment) dla starszej ścieżki property wrapperów oraz framework Observation z biblioteki standardowej (withObservationTracking, publiczny w iOS 17+) dla typów @Observable.8 Gdy śledzona właściwość jest mutowana, odpowiadające ciała są wykonywane ponownie, a maszyneria różnicująca oblicza minimalną zmianę drzewa widoków.
Obie połowy (warstwa drzewa widoków i warstwa stanu) są luźno powiązane. Drzewo widoków jest typu wartościowego i szybkie w ponownym obliczaniu. Warstwa stanu jest typu referencyjnego (dla @Observable) lub typu wartościowego ze wskaźnikiem na magazyn (dla @State) i śledzi odczyty. Razem produkują model frameworku „opisz, co powinno znaleźć się na ekranie jako funkcja stanu, a framework obliczy różnicę”.
Co teraz rozpoznajemy w komunikatach błędów
Czytanie komunikatów błędów kompilatora SwiftUI z widocznym podłożem:
„Function declares an opaque return type, but the return statements in its body do not have matching underlying types.” Dwie instrukcje return z różnymi konkretnymi typami w funkcji some View. Rozwiązanie: użyć @ViewBuilder, by result builder opakował obie w _ConditionalContent, lub opakować oba return-y w AnyView.
„The compiler is unable to type-check this expression in reasonable time.” Długie łańcuchy ciał z wieloma modyfikatorami wyczerpują sprawdzanie typów. Rozwiązanie: rozbić ciało na mniejsze właściwości obliczane lub podwidoki; każdy fragment zwracający some View upraszcza pracę inferencji.
„Cannot convert value of type ‘TupleView<…>’ to expected type ‘some View’.” Funkcja oczekująca jednego widoku otrzymała wynik ciała wieloinstrukcyjnego bez @ViewBuilder. Rozwiązanie: dodać @ViewBuilder do parametru domknięcia akceptującego treść wieloinstrukcyjną.
„Generic parameter ‘Content’ could not be inferred.” Niestandardowy kontener przyjmuje @ViewBuilder content: () -> Content, a miejsce wywołania ma puste domknięcie. Rozwiązanie: result buildery potrzebują przynajmniej jednego wyrażenia, by wyinferować Content; puste domknięcia wracają do EmptyView(), jeśli miejsce wywołania jawnie to dostarczy.
Komunikaty błędów są nieprzyjazne, ponieważ podłoże jest niewidoczne. Czytanie ich z widocznym podłożem zamienia większość z nich w „aha, result builder nie potrafi tego przekształcić” lub „aha, potrzebuję albo rozgałęzienia, albo AnyView.”
Kiedy sięgnąć poza podłoże
Kilka wzorców, których podłoże nie obsługuje czysto:
Variadyczne typy konkretne. Funkcja, która zwraca inny typ View na gałąź i której nie można opakować w rozgałęzienie result buildera, potrzebuje AnyView. Należy zaakceptować koszt (utracone różnicowanie, brak animacji) i udokumentować miejsce wywołania.
Widoki warunkowe międzyplatformowe. #if os(iOS) w czasie kompilacji działa wewnątrz ciała @ViewBuilder, ale ogranicza liczbę rozgałęzień result buildera; wieloplatformowe ciała warunkowe czasem osiągają limit „expression too complex”. Rozwiązaniem jest wyodrębnienie podwidoków per platforma do osobnych funkcji, każda zwracająca some View.
Imperatywna konstrukcja widoków. Framework oczekuje, że widoki są wyrażeniami, a nie obiektami konstruowanymi-następnie-mutowanymi. Styl UIKit „utwórz etykietę, ustaw tekst, dodaj do podwidoku” nie tłumaczy się; odpowiednikiem SwiftUI jest Text("...") typu wartościowego zwracany z ciała. Wzorce wymagające imperatywnej konstrukcji są zwykle sygnałem, że praca należy do mostu UIViewRepresentable do UIKit.
Co ten wzorzec oznacza dla aplikacji wdrażanych na iOS 26+
Trzy wnioski.
-
SwiftUI to Swift, nie magia. Result buildery, nieprzezroczyste typy zwracane i property wrappery są wszystkie w referencji języka Swift. Czytanie frameworku jako kodu Swift, a nie jako specjalnego DSL, sprawia, że zaskakujące części stają się przewidywalne.
-
some ViewiAnyViewrozwiązują różne problemy. Nieprzezroczyste typy zwracane są domyślne; wymazywanie typu jest klapą bezpieczeństwa. Sięganie poAnyViewpowinno być przypadkiem rzadkim; sięganie posome Viewplus rozgałęzienie result buildera powinno być przypadkiem powszechnym. -
Result buildery to całe DSL. Wszędzie tam, gdzie funkcja lub parametr jest
@ViewBuilder, dostępna jest składnia bez przecinków, w kształcie instrukcji. Pisanie własnych widoków kontenerowych z taką samą ergonomią jakVStackto jeden atrybut i jeden parametr domknięcia.
Niniejszy wpis warto zestawić z serią „shipped-code” w klastrze: międzyplatformowe wdrażanie SwiftUI (Return działa na pięciu platformach z jednym wspólnym rdzeniem SwiftUI); warstwa wizualna Liquid Glass; maszyna stanów Live Activities na iOS; kontrakt środowiska uruchomieniowego watchOS na Apple Watch. Hub znajduje się w Apple Ecosystem Series. Szerszy kontekst iOS-z-agentami-AI można znaleźć w przewodniku iOS Agent Development.
FAQ
Czym jest protokół View w SwiftUI?
Protokół View ma jeden wymóg: var body: some View { get }. Każdy widok SwiftUI to typ wartościowy Swift zgodny z View, z właściwością obliczaną body, która zwraca kolejny widok (lub Never dla widoków pierwotnych takich jak Text, Color, Image, EmptyView). Ciało jest oznaczone @ViewBuilder, dzięki czemu może używać składni DSL SwiftUI bez przecinków.
Co oznacza some View?
some View to nieprzezroczysty typ zwracany (Swift 5.1+). Kompilator zna typ konkretny; wywołujący widzi tylko świadka protokołu. Typy nieprzezroczyste pozwalają ciałom widoków zwracać złożone typy, takie jak VStack<TupleView<(Text, Image, Spacer)>>, bez wypisywania ich, zachowując jednocześnie optymalizację w czasie kompilacji. some View to jeden konkretny typ, nawet jeśli ten typ nie jest widoczny w miejscu wywołania.
Kiedy należy używać AnyView?
Po AnyView warto sięgnąć tylko wtedy, gdy ani rozgałęzienie result buildera @ViewBuilder (if, switch, for), ani sparametryzowane generyki nie rozwiązują problemu. Gdy zawinięty typ konkretny zmienia się między renderowaniami, istniejąca hierarchia widoków jest niszczona, a w jej miejsce tworzona nowa; to ten moment, gdy animacje się restartują, a stan widoku jest resetowany. Ponowne opakowanie tego samego konkretnego typu nie wyzwala tego zniszczenia, ale różnicowanie sterowane statycznym typem, które framework preferuje, i tak nie jest już dostępne. Jeśli ktoś łapie się na częstym sięganiu po AnyView, wzorzec, który należy zmienić, jest wyżej: warto preferować widoki sparametryzowane lub przesunąć warunek do ciała result buildera.
Czym jest @ViewBuilder i gdzie można go używać?
@ViewBuilder to result builder (funkcja języka Swift). Przekształca domknięcie z wieloma wyrażeniami w pojedynczą wartość zwracaną poprzez wstawianie wywołań buildBlock, buildEither, buildOptional itd. syntetyzowanych przez kompilator. Ciało każdego widoku SwiftUI jest domyślnie @ViewBuilder. Można zastosować @ViewBuilder do dowolnej funkcji lub parametru domknięcia, by dać wywołującym tę samą składnię DSL; VStack, Card i Section używają tego samego wzorca, by akceptować wiele dzieci.
Dlaczego ciało mojego widoku renderuje się ponownie, gdy się tego nie spodziewam?
SwiftUI ponownie wykonuje body, ilekroć dowolna właściwość stanu, którą ciało odczytuje, zostanie zmutowana. Property wrappery (@State, @Binding, @Observable, @Environment) śledzą odczyty i wyzwalają ponowne renderowania przy zapisach. Niespodziewane ponowne renderowania zwykle wynikają ze zmiany stanu widoku rodzica, zmiany wartości środowiska lub modyfikacji odczytywanej właściwości obiektu @Observable. Różnicowanie frameworku oblicza wówczas minimalną zmianę drzewa.
Źródła
-
Apple Developer, “View” i “Configuring views”. Protokół
View, typ powiązanyBodyoraz atrybut@ViewBuildernabody. ↩ -
Swift Evolution, “SE-0289: Result builders”. Propozycja językowa, która sformalizowała result buildery (wprowadzone jako
_functionBuilderw 5.1, sformalizowane jako@resultBuilderw 5.4). DefiniujebuildBlock,buildEither,buildOptional,buildArray,buildExpression,buildFinalResulti pokrewne. ↩↩↩ -
Apple Developer, “ViewBuilder” i “ForEach”. Typ result buildera, którego SwiftUI używa dla ciał widoków (variadic-generic
buildBlock,buildEither, rozpakowanie wartości opcjonalnych).ViewBuildernie udostępniabuildArray, więcForEachjest prymitywem iteracji do powtarzania widoku po kolekcji. ↩ -
Swift Evolution, “SE-0244: Opaque result types”. Słowo kluczowe
somedla nieprzezroczystych typów zwracanych, dodane w Swift 5.1. ↩ -
Apple Developer, “AnyView”. Opakowanie widoku z wymazanym typem, konstrukcja i kompromis dotyczący różnicowania. ↩
-
Apple Developer, “Group”, “EmptyView” i “TupleView”. Typy implementacyjne, które syntetyzują result buildery. ↩
-
Apple Developer, “State and Data Flow”. Warstwa property wrapperów:
@State,@Binding,@Observable,@Environment. System obserwacji SwiftUI i makro@Observablez iOS 17+. ↩ -
Apple Developer, “Observation” i “Migrating from the Observable Object protocol to the Observable macro”. Framework Observation z biblioteki standardowej, w tym
withObservationTracking(_:onChange:), plus ścieżka migracji z iOS 17 zObservableObjectna@Observable. ↩