Property Wrapper: nyckelbegrepp, syntax och exempel i SwiftUI

Författare: IT Sectr Publicerad: 2026-06-19 Lästid: 8 min

Property Wrapper är en Swift-mekanism som lägger till ett logiklager till åtkomst och ändring av egenskaper utan kodduplicering. I SwiftUI har Property Wrappers blivit grunden för hantering av vyns tillstånd: @State, @Binding, @ObservedObject, @StateObject och @Environment. Enligt Swift-dokumentationen (2025) minskar egenskapsomslag boilerplate-koden i projekt med i genomsnitt 40%. Förståelse av Property Wrapper är nödvändigt för varje iOS-utvecklare för att arbeta effektivt med ramverket.

Huvudpunkter

  • Property Wrapper — syntaktisk konstruktion i Swift som lägger till logik till egenskapens getter och setter
  • @State — omslag för lokal status som tillhör en vy
  • @Binding — tvåvägsförbindelse med datakällan från den överordnade vyn
  • @ObservedObject — observation av extern ObservableObject utan ägarskap
  • @StateObject — skapande och ägarskap av ObservableObject på den aktuella vyns nivå

Vad är Property Wrapper?

Property Wrapper är en språkkonstruktion i Swift som introducerades i version 5.1 och som gör det möjligt att kapsla in logiken för åtkomst till en egenskap i en separat typ. Istället för att skriva upprepade getters och setters i varje klass deklarerar utvecklaren omslaget en gång och applicerar det via @-annotationen före typen. Swift slår automatiskt in egenskapen i den angivna typen och anropar dess metoder wrappedValue och projectedValue vid läsning och skrivning. Enligt Apple (WWDC 2019) har Property Wrappers blivit den viktigaste abstraktionen för SwiftUI.

Ett egenskapsomslag är en struktur eller klass med attributet @propertyWrapper. Internt måste en sådan typ implementera egenskapen wrappedValue som returnerar och ställer in det aktuella värdet. Swift-kompilatorn ersätter referenser till den ursprungliga egenskapen med anrop till wrappedValue, vilket helt döljer implementeringen från den anropande koden. Dessutom kan projectedValue definieras — en projektion som är tillgänglig via $-symbolen.

Fördelen med Property Wrappers ligger i återanvändning av logik. Till exempel kan man skapa ett omslag för e-postvalidering, cachning av värden eller synkronisering med lagring — och applicera det på vilken egenskap som helst i projektet. I SwiftUI används detta koncept överallt: varje mekanism för tillståndshantering är implementerad som en separat Property Wrapper.

Hur fungerar Property Wrapper i Swift?

Vid deklaration av en egenskap med annotationen @WrapperType var value: T transformerar Swift-kompilatorn koden. Den skapar en instans av WrapperType och genererar åtkomst till egenskapen via wrappedValue. Källkoden let x = value blir let x = _value.wrappedValue, och value = newValue blir _value.wrappedValue = newValue. Denna transformation sker vid kompilering utan runtime-overhead.

swift
@propertyWrapper
struct Capitalized {
    private var text: String

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

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

I listningen visas omslaget Capitalized, som automatiskt formaterar text med versal. Vid tilldelning av värde anropar settern capitalized före lagring. Nu kommer varje egenskap med annotationen @Capitalized endast att lagra korrekt formaterad text. Detta tillvägagångssätt eliminerar helt duplicering av validerings- och formateringskod.

Projektion (projectedValue) — en extra kommunikationskanal som är tillgänglig via prefixet $. I SwiftUI används denna funktion överallt: $state ger Binding och $observedObject returnerar Published.Publisher. Projektionen tillåter omslaget att tillhandahålla ett utökat gränssnitt utan att ändra den primära åtkomsten till värdet.

Huvudsakliga Property Wrappers i SwiftUI

SwiftUI innehåller fem inbyggda Property Wrappers för tillståndshantering: @State, @Binding, @ObservedObject, @StateObject och @Environment. Var och en löser en specifik uppgift och används i olika scenarier. @State är avsett för enkla lokala data, @Binding — för att skicka en referens till data till underordnade vyer, @ObservedObject och @StateObject — för komplexa objekt, @Environment — för systemvärden från hierarkin.

WrapperÄndamålÄgarskap
@StateLokal status för en vyAktuell vy
@BindingTvåvägsförbindelse med förälderÖverordnad vy
@ObservedObjectObservation av externt objektExtern ägare
@StateObjectSkapa ObservableObjectAktuell vy
@EnvironmentSystemvärden från hierarkinSwiftUI-miljö

Valet av en specifik Property Wrapper beror på datakällan och dess livscykel. Om data tillhör en vy och inte behövs av underordnade komponenter — använd @State. Om en underordnad vy behöver ändra förälderns data — applicera @Binding. För objekt som används i flera vyer är @ObservedObject och @StateObject lämpliga.

@State: lokal status för en vy

@State är en Property Wrapper för att lagra lokal status inom en vy. SwiftUI hanterar automatiskt minnet för @State-egenskaper och ritar om vyn vid varje ändring. @State är lämplig för enkla typer (String, Int, Bool, enum) och strukturer som uteslutande tillhör den aktuella vyn. När värdet ändras kör SwiftUI om body-egenskapen.

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

    var body: some View {
        VStack {
            Text("Antal: \(count)")
            Button("Öka") {
                count += 1
            }
        }
    }
}

I exemplet lagrar @State-egenskapen count det aktuella värdet för räknaren. SwiftUI skapar ett lagringsområde för denna egenskap på heapen och kopplar det till CounterViews livscykel. När knappen trycks in ökar count med 1, SwiftUI upptäcker ändringen och kör om body, vilket visar det nya värdet. Viktigt: @State bör inte användas för komplexa referenstyper — för det ändamålet är @StateObject och @ObservedObject avsedda.

@Binding: tvåvägsförbindelse mellan vyer

@Binding skapar en referens till en datakälla som tillhör en annan vy. Binding lagrar inte värdet självständigt — den läser och skriver data via @State, @StateObject eller en annan Binding som skickats från föräldern. Detta tillåter underordnade komponenter att ändra förfaderns status utan direkt ägarskap av data och utan callbacks.

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

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

I listningen tar ToggleSwitch emot en @BindingBool från den överordnade vyn. Föräldern skapar @State var isToggleOn = false och skickar $isToggleOn till ToggleSwitches initierare. När användaren växlar Toggle inuti den underordnade vyn reflekteras ändringen omedelbart i förälderns @State. Binding-mekanismen eliminerar helt behovet av delegater eller closures för att skicka ändringar uppåt i hierarkin.

@ObservedObject och @StateObject: extern data

@ObservedObject är en Property Wrapper för att observera en ObservableObject-instans som skickats utifrån. Vyn äger inte detta objekt — det skapas i den överordnade komponenten eller injiceras via Environment. När någon @Published-egenskap inuti ObservableObject ändras, ritar SwiftUI om alla vyer som prenumererar via @ObservedObject.

@StateObject är ett omslag för att skapa och äga ObservableObject direkt i vyn. Till skillnad från @ObservedObject garanterar @StateObject en enda instans av objektet under hela vyns livscykel. Även om SwiftUI återskapar vystrukturen (vilket händer ofta), behåller @StateObject det befintliga objektet och anropar inte initieraren igen.

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("Hej, \(settings.username)")
    }
}

I exemplet skapar ProfileView UserSettings via @StateObject och blir ägare av objektet. ChildProfileView tar emot samma instans via @ObservedObject — observerar men hanterar inte livscykeln. När username ändras uppdateras båda vyerna. Om ChildProfileView hade använt @StateObject istället för @ObservedObject skulle en ny instans med startvärdet skapas vid varje rendering.

Nyckelregeln: @StateObject tillämpas i vyn som skapar objektet (sanningens källa), och @ObservedObject i vyn som tar emot det färdiga objektet från föräldern. Brott mot denna regel leder till statusförlust eller oväntad återskapning av data.

Skapa en egen Property Wrapper

Swift tillåter att skapa anpassade Property Wrappers för vilken repetitiv logik som helst för egenskapsåtkomst. Det räcker att deklarera en struktur eller klass med attributet @propertyWrapper och implementera wrappedValue. Nedan visas omslaget UserDefaultsWrapper som automatiskt synkroniserar värdet med 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
}

Omslaget UserDefaultsWrapper använder en generisk T för att arbeta med vilken datatyp som helst som stöds av UserDefaults. Gettern läser värdet baserat på en nyckel, settern skriver. Tillämpning av @UserDefaultsWrapper(key:defaultValue:) på egenskapen theme kopplar den automatiskt till lagringen — all logik för arbete med UserDefaults är gömd inuti omslaget. Detta är ett typiskt exempel på att minska boilerplate-kod med Property Wrappers.

När du skapar egna omslag är det viktigt att ta hänsyn till prestanda. Eftersom gettern och settern anropas vid varje åtkomst till egenskapen bör tunga I/O-operationer inte placeras i wrappedValue. För asynkron datalagring är det bättre att kombinera Property Wrappers med ObservableObject och @Published.

Vanliga frågor

Vad är skillnaden mellan @State och @StateObject?

@State är avsett för enkla typer (String, Int, Bool) och strukturer, medan @StateObject är för referenstyper som implementerar ObservableObject. @State lagrar värdet direkt i SwiftUI, @StateObject hanterar en klassinstans på heapen.

Kan @Binding användas utan @State?

Ja, @Binding kan skapas från @StateObject, @ObservedObject eller från en annan Binding med hjälp av $-projektionen. Binding initieras också från ObservableObject via $object.$publishedProperty eller från InlineBinding via Binding.constant(value).

Vilken Property Wrapper ska man välja för data som används i hela appen?

För global data, använd @EnvironmentObject eller injicera ObservableObject via EnvironmentValues. @StateObject är lämplig för rotvyn med efterföljande vidarebefordran via @ObservedObject till underordnade komponenter.

Varför kan @ObservedObject återskapa data?

@ObservedObject äger inte objektet — om den överordnade vyn återskapas och skickar en ny instans, växlar @ObservedObject till den. För att undvika statusförlust bör den ägande vyn använda @StateObject.

Kan man skapa en Property Wrapper för asynkrona operationer?

Ja, men det är enklare att använda en kombination av ObservableObject med @Published och async-funktioner inuti klassen. Property Wrapper är synkron till sin natur — wrappedValue beräknas vid varje åtkomst, vilket inte är lämpligt för långvariga operationer.

Sammanfattning

  • Property Wrapper — syntaktisk konstruktion i Swift 5.1 för att kapsla in åtkomstlogik till egenskaper utan kodduplicering
  • @State — lokal status för enkla typer, ritar automatiskt om vyn vid ändring
  • @Binding — referens till överordnad vyns data utan ägarskap, tillgänglig via $-projektion
  • @StateObject — ägarskap av ObservableObject med garanti för en enda instans under livscykeln
  • @ObservedObject — observation av ObservableObject som skickats från överordnad komponent
  • @Environment — åtkomst till systemobjekt i SwiftUI-miljön
  • Anpassade omslag skapas via @propertyWrapper-attributet och implementering av wrappedValue

Vi utvecklar en mobil applikation nyckelfärdigt

IT Sectr skapar iOS- och Android-applikationer för startups och företag sedan 2017. Vi ger dig råd och föreslår den bästa lösningen.

Diskutera projektet

Läs också