Property Wrapper: klíčové pojmy, syntaxe a příklady ve SwiftUI

Autor: IT Sectr Publikováno: 2026-06-19 Doba čtení: 8 min

Property Wrapper je mechanismus Swift, který přidává vrstvu logiky k přístupu a změně vlastností bez duplicitního kódu. Ve SwiftUI se Property Wrappers staly základem správy stavu zobrazení: @State, @Binding, @ObservedObject, @StateObject a @Environment. Podle dokumentace Swift (2025) snižují obaly vlastností boilerplate kód v projektech v průměru o 40%. Porozumění Property Wrapper je nezbytné pro každého iOS vývojáře pro efektivní práci s frameworkem.

Hlavní body

  • Property Wrapper — syntaktická konstrukce Swift přidávající logiku do getteru a setteru vlastnosti
  • @State — obal pro lokální stav patřící jednomu zobrazení
  • @Binding — obousměrné propojení se zdrojem dat z nadřazeného zobrazení
  • @ObservedObject — pozorování externího ObservableObject bez vlastnictví
  • @StateObject — vytvoření a vlastnictví ObservableObject na úrovni aktuálního zobrazení

Co je Property Wrapper?

Property Wrapper je jazyková konstrukce Swift, představená ve verzi 5.1, která umožňuje zapouzdřit logiku přístupu k vlastnosti do samostatného typu. Místo psaní opakujících se getterů a setterů v každé třídě vývojář deklaruje obal jednou a aplikuje ho pomocí anotace @ před typem. Swift automaticky obalí vlastnost do zadaného typu a při čtení a zápisu volá jeho metody wrappedValue a projectedValue. Podle Apple (WWDC 2019) se Property Wrappers staly klíčovou abstrakcí pro SwiftUI.

Obal vlastnosti je struktura nebo třída s atributem @propertyWrapper. Uvnitř takový typ musí implementovat vlastnost wrappedValue, která vrací a nastavuje aktuální hodnotu. Překladač Swift nahrazuje odkazy na původní vlastnost voláními wrappedValue, čímž zcela skrývá implementaci před volajícím kódem. Dodatečně lze definovat projectedValue — projekci přístupnou přes symbol $.

Výhoda Property Wrappers spočívá v opětovném použití logiky. Například lze vytvořit obal pro validaci e-mailu, ukládání hodnot do mezipaměti nebo synchronizaci s úložištěm — a aplikovat ho na libovolnou vlastnost v projektu. Ve SwiftUI je tento koncept používán všude: každý mechanismus správy stavu je implementován jako samostatný Property Wrapper.

Jak funguje Property Wrapper ve Swift?

Při deklaraci vlastnosti s anotací @WrapperType var value: T překladač Swift transformuje kód. Vytvoří instanci WrapperType a generuje přístup k vlastnosti přes wrappedValue. Zdrojový kód let x = value se změní na let x = _value.wrappedValue a value = newValue na _value.wrappedValue = newValue. Tato transformace probíhá ve fázi překladu bez runtime režie.

swift
@propertyWrapper
struct Capitalized {
    private var text: String

    var wrappedValue: String {
        get { text }
        set { text = newValue.capitalized }
    }

    init(initialValue: String) {
        text = initialValue.capitalized
    }
}

Ve výpisu je zobrazen obal Capitalized, který automaticky formátuje text s velkým písmenem. Při přiřazení hodnoty setter zavolá capitalized před uložením. Nyní bude jakákoli vlastnost s anotací @Capitalized ukládat pouze správně naformátovaný text. Tento přístup zcela eliminuje duplicitu validačního a formátovacího kódu.

Projekce (projectedValue) — dodatečný komunikační kanál přístupný přes prefix $. Ve SwiftUI je tato možnost používána všude: $state poskytuje Binding a $observedObject vrací Published.Publisher. Projekce umožňuje obalu poskytovat rozšířené rozhraní bez změny hlavního přístupu k hodnotě.

Hlavní Property Wrappers ve SwiftUI

SwiftUI obsahuje pět vestavěných Property Wrappers pro správu stavu: @State, @Binding, @ObservedObject, @StateObject a @Environment. Každý z nich řeší konkrétní úkol a používá se v různých scénářích. @State je určen pro jednoduchá lokální data, @Binding — pro předávání reference na data do podřízených zobrazení, @ObservedObject a @StateObject — pro složité objekty, @Environment — pro systémové hodnoty z hierarchie.

WrapperÚčelVlastnictví
@StateLokální stav jednoho zobrazeníAktuální zobrazení
@BindingObousměrné propojení s rodičemNadřazené zobrazení
@ObservedObjectPozorování externího objektuExterní vlastník
@StateObjectVytvoření ObservableObjectAktuální zobrazení
@EnvironmentSystémové hodnoty z hierarchieProstředí SwiftUI

Výběr konkrétního Property Wrapper závisí na zdroji dat a jejich životním cyklu. Pokud data patří jednomu zobrazení a nejsou potřeba pro podřízené komponenty — použijte @State. Pokud podřízené zobrazení potřebuje měnit data rodiče — aplikujte @Binding. Pro objekty používané ve více zobrazeních jsou vhodné @ObservedObject a @StateObject.

@State: lokální stav zobrazení

@State je Property Wrapper pro ukládání lokálního stavu uvnitř jednoho zobrazení. SwiftUI automaticky spravuje paměť pro @State vlastnosti a překresluje zobrazení při každé změně. @State je vhodný pro jednoduché typy (String, Int, Bool, enum) a struktury, které patří výhradně aktuálnímu zobrazení. Při změně hodnoty SwiftUI znovu spustí vlastnost body.

swift
struct CounterView: View {
    @State private var count: Int = 0

    var body: some View {
        VStack {
            Text("Počet: \(count)")
            Button("Zvýšit") {
                count += 1
            }
        }
    }
}

V příkladu vlastnost @State count ukládá aktuální hodnotu počítadla. SwiftUI vytváří úložný prostor pro tuto vlastnost na haldě a propojuje ho s životním cyklem CounterView. Při stisknutí tlačítka se count zvýší o 1, SwiftUI detekuje změnu a znovu spustí body, čímž zobrazí novou hodnotu. Důležité: @State by se neměl používat pro složité referenční typy — k tomu slouží @StateObject a @ObservedObject.

@Binding: obousměrné propojení mezi zobrazeními

@Binding vytváří referenci na zdroj dat patřící jinému zobrazení. Binding neukládá hodnotu samostatně — čte a zapisuje data přes @State, @StateObject nebo jiný Binding předaný od rodiče. To umožňuje podřízeným komponentám měnit stav předka bez přímého vlastnictví dat a bez callbacků.

swift
struct ToggleSwitch: View {
    @Binding var isOn: Bool

    var body: some View {
        Toggle("Switch", isOn: $isOn)
    }
}

Ve výpisu ToggleSwitch přijímá @BindingBool od nadřazeného zobrazení. Rodič vytvoří @State var isToggleOn = false a předá $isToggleOn inicializátoru ToggleSwitch. Když uživatel přepne Toggle uvnitř podřízeného zobrazení, změna se okamžitě projeví v rodičovském @State. Mechanismus Binding zcela eliminuje potřebu delegátů nebo closures pro předávání změn nahoru hierarchií.

@ObservedObject a @StateObject: externí data

@ObservedObject je Property Wrapper pro pozorování instance ObservableObject předané zvenčí. Zobrazení není vlastníkem tohoto objektu — je vytvořen v nadřazené komponentě nebo injektován přes Environment. Když se jakákoli @Published vlastnost uvnitř ObservableObject změní, SwiftUI překreslí všechna zobrazení přihlášená přes @ObservedObject.

@StateObject je obal pro vytvoření a vlastnictví ObservableObject přímo v zobrazení. Na rozdíl od @ObservedObject, @StateObject garantuje jedinou instanci objektu po celý životní cyklus zobrazení. I když SwiftUI znovu vytvoří strukturu zobrazení (což se stává často), @StateObject zachová existující objekt a znovu nezavolá inicializátor.

swift
class UserSettings: ObservableObject {
    @Published var username: String = "Guest"
}

struct ProfileView: View {
    @StateObject var settings = UserSettings()

    var body: some View {
        ChildProfileView(settings: settings)
    }
}

struct ChildProfileView: View {
    @ObservedObject var settings: UserSettings

    var body: some View {
        Text("Ahoj, \(settings.username)")
    }
}

V příkladu ProfileView vytváří UserSettings přes @StateObject a stává se vlastníkem objektu. ChildProfileView přijímá stejnou instanci přes @ObservedObject — pozoruje, ale nespravuje životní cyklus. Při změně username se obě zobrazení aktualizují. Pokud by ChildProfileView použil @StateObject místo @ObservedObject, při každém renderu by se vytvářela nová instance s počáteční hodnotou.

Klíčové pravidlo: @StateObject se aplikuje v zobrazení, které objekt vytváří (zdroj pravdy), a @ObservedObject v zobrazení, které přijímá hotový objekt od rodiče. Porušení tohoto pravidla vede ke ztrátě stavu nebo neočekávanému opětovnému vytváření dat.

Vytvoření vlastního Property Wrapper

Swift umožňuje vytvářet vlastní Property Wrappers pro jakoukoli opakující se logiku přístupu k vlastnostem. Stačí deklarovat strukturu nebo třídu s atributem @propertyWrapper a implementovat wrappedValue. Níže je zobrazen obal UserDefaultsWrapper, který automaticky synchronizuje hodnotu s UserDefaults.

swift
@propertyWrapper
struct UserDefaultsWrapper<T> {
    let key: String
    let defaultValue: T

    var wrappedValue: T {
        get { UserDefaults.standard.object(forKey: key) as? T ?? defaultValue }
        set { UserDefaults.standard.set(newValue, forKey: key) }
    }
}

struct AppConfig {
    @UserDefaultsWrapper(key: "theme", defaultValue: "light")
    var theme: String
}

Obal UserDefaultsWrapper používá generic T pro práci s libovolným datovým typem podporovaným UserDefaults. Getter čte hodnotu podle klíče, setter zapisuje. Aplikace @UserDefaultsWrapper(key:defaultValue:) na vlastnost theme ji automaticky propojí s úložištěm — veškerá logika práce s UserDefaults je skryta uvnitř obalu. To je typický příklad snížení boilerplate kódu pomocí Property Wrappers.

Při vytváření vlastních obalů je důležité zohlednit výkon. Protože getter a setter jsou volány při každém přístupu k vlastnosti, neměly by být do wrappedValue umísťovány těžké I/O operace. Pro asynchronní ukládání dat je lepší kombinovat Property Wrappers s ObservableObject a @Published.

Často kladené otázky

Jaký je rozdíl mezi @State a @StateObject?

@State je určen pro jednoduché typy (String, Int, Bool) a struktury, zatímco @StateObject pro referenční typy implementující ObservableObject. @State ukládá hodnotu přímo ve SwiftUI, @StateObject spravuje instanci třídy na haldě.

Lze použít @Binding bez @State?

Ano, @Binding lze vytvořit z @StateObject, @ObservedObject nebo z jiného Binding pomocí projekce $. Binding se také inicializuje z ObservableObject přes $object.$publishedProperty nebo z InlineBinding přes Binding.constant(value).

Který Property Wrapper zvolit pro data používaná v celé aplikaci?

Pro globální data použijte @EnvironmentObject nebo injektujte ObservableObject přes EnvironmentValues. @StateObject je vhodný pro kořenové zobrazení s následným předáváním přes @ObservedObject podřízeným komponentám.

Proč může @ObservedObject znovu vytvářet data?

@ObservedObject není vlastníkem objektu — pokud je nadřazené zobrazení znovu vytvořeno a předá novou instanci, @ObservedObject na ni přepne. Aby se předešlo ztrátě stavu, mělo by vlastnící zobrazení použít @StateObject.

Lze vytvořit Property Wrapper pro asynchronní operace?

Ano, ale jednodušší je použít kombinaci ObservableObject s @Published a async funkcemi uvnitř třídy. Property Wrapper je ze své podstaty synchronní — wrappedValue se vypočítává při každém přístupu, což není vhodné pro dlouhodobé operace.

Shrnutí

  • Property Wrapper — syntaktická konstrukce Swift 5.1 pro zapouzdření logiky přístupu k vlastnostem bez duplicitního kódu
  • @State — lokální stav pro jednoduché typy, automaticky překresluje zobrazení při změně
  • @Binding — reference na data nadřazeného zobrazení bez vlastnictví, přístupná přes projekci $
  • @StateObject — vlastnictví ObservableObject s garancí jediné instance po dobu životního cyklu
  • @ObservedObject — pozorování ObservableObject předaného z nadřazené komponenty
  • @Environment — přístup k systémovým objektům prostředí SwiftUI
  • Vlastní obaly se vytvářejí pomocí atributu @propertyWrapper a implementace wrappedValue

Vyvineme mobilní aplikaci na klíč

IT Sectr vytváří aplikace pro iOS a Android pro startupy a podniky od roku 2017. Poradíme vám a navrhneme nejlepší řešení.

Prodiskutovat projekt

Přečtěte si také