@AppStorage w SwiftUI — co to jest, UserDefaults i przechowywanie ustawień

Autor: IT Sectr Opublikowano: 2026-06-25 Czas czytania: 7 min

@AppStorage w SwiftUI — property wrapper do pracy z UserDefaults, który automatycznie synchronizuje wartość z interfejsem użytkownika. Podczas zmiany właściwości zadeklarowanej przez @AppStorage nowa wartość jest natychmiast zapisywana w UserDefaults, a gdy UserDefaults zmienia się z zewnątrz — przez widget lub rozszerzenie — widok automatycznie się przerysowuje. Według Apple Developer Documentation (2025), @AppStorage obsługuje String, Int, Double, Bool, Data, URL i ich opcjonalne wersje, zapewniając reaktywne przechowywanie ustawień użytkownika bez ręcznego kodu obserwacji.

Najważniejsze

  • @AppStorage — property wrapper do reaktywnej pracy z UserDefaults w SwiftUI
  • Autozapis — wartość zapisywana jest w UserDefaults przy każdej zmianie
  • Automatyczne odświeżanie UI — widok przerysowuje się przy zmianie UserDefaults z dowolnego źródła
  • Obsługiwane typy: String, Int, Double, Bool, Data, URL i wersje opcjonalne
  • Wartość domyślna ustawiana jest w deklaracji i używana przy pierwszym uruchomieniu

Czym jest @AppStorage w SwiftUI?

@AppStorage to property wrapper wprowadzony przez Apple w iOS 14, który łączy właściwość widoku z kluczem w UserDefaults. Podczas odczytu właściwości SwiftUI ładuje wartość z UserDefaults na podstawie podanego klucza. Podczas zapisu — zapisuje nową wartość i powiadamia widok o konieczności przerysowania.

Przed pojawieniem się @AppStorage programiści musieli ręcznie odczytywać UserDefaults w onAppear, subskrybować powiadomienie UserDefaults.didChangeNotification i aktualizować @State przy zmianach. @AppStorage automatyzuje cały cykl: deklaracja w jednej linii zastępuje 15–20 linii kodu boilerplate. Co więcej, @AppStorage zapewnia dwukierunkową synchronizację — jeśli wartość UserDefaults zostanie zmieniona z innego procesu (np. App Extension lub Widget), widok i tak otrzyma aktualizację.

Architektonicznie @AppStorage jest zaimplementowany jako DynamicProperty, co pozwala SwiftUI śledzić zależności i przerysowywać widok przy zmianie obserwowanej wartości. To czyni go idealnym do przechowywania ustawień użytkownika: język interfejsu, włączanie/wyłączanie funkcji, ostatnio wybrana zakładka, nazwa użytkownika.

@AppStorage vs UserDefaults: porównanie

Chociaż @AppStorage używa UserDefaults pod maską, podejścia do pracy z magazynem różnią się zasadniczo. UserDefaults to niskopoziomowe API wymagające ręcznego zarządzania odczytem, zapisem i powiadomieniami o zmianach. @AppStorage to abstrakcja SwiftUI zapewniająca reaktywne zachowanie od razu po wyjęciu z pudełka.

UserDefaults nadaje się do jednorazowych operacji: ładowania ustawień przy starcie aplikacji, zapisywania analityki, buforowania tokenów. @AppStorage — do ustawień, które powinny reaktywnie aktualizować UI: przełączniki motywów, wybór języka, zapisywanie stanu interfejsu. Bezpośrednie używanie UserDefaults wewnątrz widoku to antywzorzec, ponieważ widok nie dowiaduje się o zmianach bez dodatkowej subskrypcji.

Parametr@AppStorageUserDefaults
ReaktywnośćAutomatycznaWymaga subskrypcji powiadomień
Boilerplate1 linia na właściwość15–20 linii na właściwość
TypyString, Int, Double, Bool, Data, URLWszystkie typy + obiekty archiwalne
Typy niestandardowePrzez RawRepresentablePrzez NSKeyedArchiver
App ExtensionAutomatyczna synchronizacjaRęczna subskrypcja

Dla prostych ustawień z reaktywnym UI @AppStorage jest preferowanym wyborem. Dla złożonych danych (tablice, słowniki, niestandardowe obiekty) użyj kombinacji UserDefaults z @State i ręczną subskrypcją zmian lub przejdź na SwiftData / Core Data do strukturalnego przechowywania.

Obsługiwane typy danych

@AppStorage obsługuje standardowe typy, które UserDefaults może serializować bezpośrednio: String, Int, Double, Bool, Data, URL. Dla każdego typu istnieje wersja opcjonalna (String?, Int?, Double?, Bool?, Data?, URL?), pozwalająca rozróżnić „nie ustawiono“ i „pusta wartość“.

Do przechowywania niestandardowych typów zgodnych z protokołem RawRepresentable, @AppStorage również działa automatycznie. Jeśli enum ma rawValue typu String lub Int, można go używać bezpośrednio: @AppStorage("theme") var theme: AppTheme = .system. SwiftUI automatycznie serializuje/deserializuje wartość przez rawValue.

swift
enum AppTheme: String {
    case system, light, dark
}

struct SettingsView: View {
    @AppStorage("username") var username: String = "Guest"
    @AppStorage("launchCount") var launchCount: Int = 0
    @AppStorage("isDarkMode") var isDarkMode: Bool = false
    @AppStorage("appTheme") var theme: AppTheme = .system
    @AppStorage("lastOpened") var lastOpened: Date? = nil

    var body: some View {
        Form {
            TextField("Username", text: $username)
            Toggle("Dark mode", isOn: $isDarkMode)
            Text("Uruchomiono \(launchCount) razy")
        }
    }
}

W przykładzie użyto różnych typów @AppStorage: String z domyślną wartością „Guest“, Int do licznika uruchomień, Bool do ciemnego motywu, enum AppTheme z rawValue typu String i opcjonalny Date? do czasu ostatniego otwarcia. Każda właściwość jest powiązana z kluczem UserDefaults podanym jako pierwszy argument. Wartość domyślna jest używana, jeśli klucz nie istnieje w magazynie przy pierwszym uruchomieniu.

Obserwacja zmian Store

Jedną z kluczowych zalet @AppStorage jest automatyczne obserwowanie zmian UserDefaults z dowolnego źródła. Jeśli App Extension lub Widget zmieni wartość, @AppStorage w nadrzędnej aplikacji otrzymuje powiadomienie i przerysowuje widok. Jest to osiągane przez mechanizm KVO (Key-Value Observing), który @AppStorage automatycznie ustawia na UserDefaults.didChangeNotification.

W praktyce oznacza to, że jeśli użytkownik zmieni ustawienie w Widget (np. włączy ciemny motyw), aplikacja natychmiast przechwyci tę zmianę. Podobnie działa synchronizacja między główną aplikacją a Share Extension, Watch App lub Today Widget. Deweloper nie musi pisać kodu do międzyprocesowej wymiany danych — @AppStorage robi to automatycznie.

swift
struct ThemeSettingView: View {
    @AppStorage("isDarkMode") var isDarkMode: Bool = false

    var body: some View {
        VStack {
            Toggle("Dark Mode", isOn: $isDarkMode)
                .onChange(of: isDarkMode) { oldValue, newValue in
                    print("Tryb ciemny zmieniony na \(newValue)")
                }
        }
    }
}

Toggle jest powiązany z $isDarkMode przez @AppStorage. Przy przełączeniu wartość jest automatycznie zapisywana w UserDefaults pod kluczem „isDarkMode“. Modyfikator .onChange pozwala wykonać dodatkową akcję przy zmianie — na przykład wysłać analitykę lub zaktualizować UI innych ekranów. Jeśli Widget zmieni ten sam klucz, @AppStorage również wywoła onChange, zapewniając spójność stanu.

Przykłady kodu @AppStorage

Rozważmy pełny ekran ustawień aplikacji używający @AppStorage do przechowywania wszystkich konfiguracji. Formularz zawiera sekcje z różnymi typami ustawień: pola tekstowe, przełączniki, liczniki — wszystkie wartości są automatycznie zapisywane w UserDefaults.

swift
struct AppSettingsView: View {
    @AppStorage("displayName") var displayName = ""
    @AppStorage("notificationsEnabled") var notificationsEnabled = true
    @AppStorage("maxResults") var maxResults = 25
    @AppStorage("selectedTab") var selectedTab = "home"

    var body: some View {
        NavigationStack {
            Form {
                Section(header: Text("Profil")) {
                    TextField("Display name", text: $displayName)
                }

                Section(header: Text("Preferencje")) {
                    Toggle("Enable notifications",
                           isOn: $notificationsEnabled)
                    Stepper("Max results: \(maxResults)",
                           value: $maxResults,
                           in: 10...100,
                           step: 5)
                }

                Section {
                    Button("Resetuj ustawienia") {
                        UserDefaults.standard.removePersistentDomain(
                            forName: Bundle.main.bundleIdentifier!)
                    }
                    .tint(.red)
                }
            }
            .navigationTitle("Settings")
        }
    }
}

Formularz zawiera cztery właściwości @AppStorage różnych typów: String dla nazwy, Bool dla powiadomień, Int dla liczby wyników i String dla wybranej zakładki. Wszystkie kontrolki są powiązane z właściwościami przez Binding ($displayName, $notificationsEnabled itd.). Przycisk „Reset settings“ resetuje wszystkie UserDefaults, usuwając domenę aplikacji — po tym @AppStorage automatycznie wróci do wartości domyślnych.

Synchronizacja @AppStorage z App Group

swift
struct SharedSettingsView: View {
    let sharedDefaults = UserDefaults(suiteName: "group.com.example.app")

    @AppStorage("widgetTheme", store: UserDefaults(suiteName: "group.com.example.app")!)
    var widgetTheme: String = "system"

    @AppStorage("widgetColor", store: UserDefaults(suiteName: "group.com.example.app")!)
    var widgetColor: String = "niebieski"

    var body: some View {
        Form {
            Picker("Widget theme", selection: $widgetTheme) {
                Text("System").tag("system")
                Text("Jasny").tag("jasny")
                Text("Ciemny").tag("ciemny")
            }
            Picker("Accent color", selection: $widgetColor) {
                Text("Niebieski").tag("blue")
                Text("Zielony").tag("zielony")
                Text("Czerwony").tag("czerwony")
            }
        }
    }
}

Dla App Group (wspólnego magazynu między aplikacją a rozszerzeniami) @AppStorage przyjmuje parametr store: UserDefaults(suiteName:). Wartości są zapisywane we wspólnym kontenerze dostępnym dla głównej aplikacji, Widget, Watch App i innych rozszerzeń tej samej grupy. Widget może odczytywać te ustawienia, a przy zmianie w aplikacji — Widget automatycznie aktualizuje się przez mechanizm obserwacji UserDefaults.

Często zadawane pytania

Jaka jest różnica między @AppStorage a @State?

@State przechowuje wartość tylko w pamięci i resetuje się przy ponownym uruchomieniu aplikacji. @AppStorage zapisuje wartość w UserDefaults i przywraca ją przy następnym uruchomieniu. Używaj @State do tymczasowych danych ekranu, @AppStorage — do ustawień, które powinny przetrwać ponowne uruchomienie.

Czy można używać @AppStorage z Enum?

Tak, jeśli Enum implementuje protokół RawRepresentable z rawValue typu String lub Int. Przykład: @AppStorage("theme") var theme: AppTheme = .system. SwiftUI automatycznie serializuje enum przez rawValue i przywraca przy ładowaniu.

Jak wyczyścić wszystkie wartości @AppStorage?

Wywołaj UserDefaults.standard.removePersistentDomain(forName: Bundle.main.bundleIdentifier!) dla standardowego magazynu lub removeObject(forKey:) dla konkretnego klucza. Po wyczyszczeniu wszystkie właściwości @AppStorage wrócą do domyślnych wartości podanych w deklaracji.

Czy @AppStorage działa z App Extensions?

Tak, do synchronizacji między aplikacją a rozszerzeniami użyj App Group: @AppStorage("key", store: UserDefaults(suiteName: "group.com.example.app")!). Widget, Share Extension i Watch App mogą odczytywać i zapisywać w tym samym UserDefaults, a zmiany są automatycznie śledzone.

Jaką ilość danych można przechowywać w @AppStorage?

@AppStorage używa UserDefaults, który jest przeznaczony do niewielkich ilości danych: ustawień, tokenów, liczników. Zalecany limit — do 100 KB na aplikację. Do strukturalnych lub dużych danych (tablice obiektów, pliki multimedialne) używaj SwiftData, Core Data lub systemu plików.

Podsumowanie

  • @AppStorage — property wrapper do reaktywnego przechowywania ustawień w UserDefaults
  • Autozapis i automatyczne odświeżanie UI przy zmianie wartości z dowolnego źródła
  • Obsługuje String, Int, Double, Bool, Data, URL i RawRepresentable enums
  • Wartość domyślna ustawiana jest w deklaracji i przywracana przy pierwszym uruchomieniu
  • App Group pozwala synchronizować @AppStorage między aplikacją a rozszerzeniami
  • UserDefaults nadaje się tylko do niewielkich ilości danych — do 100 KB
  • Używaj @AppStorage do ustawień użytkownika, @State — do tymczasowych stanów ekranu

Opracujemy aplikację mobilną pod klucz

IT Sectr tworzy aplikacje na iOS i Androida dla startupów i firm od 2017 roku. Doradzimy Ci i zaproponujemy najlepsze rozwiązanie.

Omów projekt

Przeczytaj również