@AppStorage в SwiftUI — какво е това, UserDefaults и съхранение на настройки

Автор: IT Sectr Публикувано: 2026-06-25 Време за четене: 7 мин

@AppStorage в SwiftUI — property wrapper за работа с UserDefaults, който автоматично синхронизира стойността с UI. При промяна на свойство, декларирано чрез @AppStorage, новата стойност незабавно се запазва в UserDefaults, а когато UserDefaults се промени отвън — от уиджет или разширение — View автоматично се прерисува. Според Apple Developer Documentation (2025), @AppStorage поддържа String, Int, Double, Bool, Data, URL и тяхните опционални версии, осигурявайки реактивно съхранение на потребителски настройки без ръчен код за наблюдение.

Основни поенти

  • @AppStorage — property wrapper за реактивна работа с UserDefaults в SwiftUI
  • Автоматично запазване — стойността се записва в UserDefaults при всяка промяна
  • Автоматично обновяване UI — View се прерисува при промяна на UserDefaults от всякаква места
  • Поддържани типове: String, Int, Double, Bool, Data, URL и опционални версии
  • Стойност по подразбиране се задава в декларацията и се използва при първото пускане

Какво е @AppStorage в SwiftUI?

@AppStorage е property wrapper, въведен от Apple в iOS 14, който свързва свойство на View с ключ в UserDefaults. При четене на свойството SwiftUI зарежда стойност от UserDefaults въз основа на посочения ключ. При запис — запазва новата стойност и уведомява View за необходимостта от прерисуване.

Преди появата на @AppStorage разработчиците трябваше ръчно да четат UserDefaults в onAppear, да се абонират за UserDefaults.didChangeNotification и да актуализират @State при промени. @AppStorage автоматизира целия цикъл: декларация на един ред заменя 15–20 реда boilerplate код. Освен това, @AppStorage осигурява двупосочна синхронизация — ако стойността на UserDefaults се промени от друг процес (напр. App Extension или Widget), View пак ще получи актуализация.

Архитектурно, @AppStorage е имплементиран като DynamicProperty, което позволява на SwiftUI да проследява зависимостите и да прерисува View при промяна на наблюдаваната стойност. Това го прави идеален за съхранение на потребителски настройки: език на интерфейса, включване/изключване на функции, последен избран раздел, потребителско име.

@AppStorage vs UserDefaults: сравнение

Въпреки че @AppStorage използва UserDefaults под капака, подходите към работа с хранилището се различават коренно. UserDefaults е нискокому ниво API, което изисква ръчно управление на четене, запис и уведомления за промени. @AppStorage е SwiftUI абстракция, която предоставя реактивно поведение направо извад кутията.

UserDefaults е подходящ за еднократни операции: зареждане на настройки при стартиране на приложението, запис на аналитика, кеширане на токени. @AppStorage — за настройки, които трябва да актуализират UI реактивно: прекълючватели на теми, избор на език, запазване на състоянието на интерфейса. Прякото използване на UserDefaults внутрен на View е антишаблон, тъй като View не разбира за промените без допълнително абониране.

Параметър@AppStorageUserDefaults
РеактивностАвтоматичнаИзисква абониране за уведомления
Boilerplate1 ред за свойство15–20 реда за свойство
ТиповеString, Int, Double, Bool, Data, URLВсички типове + архивирани обекти
Потребителски типовеЧрез RawRepresentableЧрез NSKeyedArchiver
App ExtensionАвтоматична синхронизацияРъчно абониране

За прости настройки с реактивен UI @AppStorage е предпочитаният избор. За сложни данни (масиви, речници, потребителски обекти) използвайте комбинация от UserDefaults с @State и ръчно абониране за промени, или преминете към SwiftData / Core Data за структурирано съхранение.

Поддържани типове данни

@AppStorage поддържа стандартни типове, които UserDefaults може да сериализира пряко: String, Int, Double, Bool, Data, URL. За всеки тип съществува опционална версия (String?, Int?, Double?, Bool?, Data?, URL?), позволяваща разграничаване между „nе е зададено” и „празна стойност”.

За съхранение на потребителски типове, следващи протокола RawRepresentable, @AppStorage също работи автоматично. Ако enum има rawValue от тип String или Int, може да се използва директно: @AppStorage("theme") var theme: AppTheme = .system. SwiftUI автоматично сериализира/десериализира стойността чрез 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("Пуснато \(launchCount) пъти")
        }
    }
}

В примера са използвани различни @AppStorage типове: String с стойност по подразбиране „Guest”, Int за брояч на пусканията, Bool за тъмна тема, enum AppTheme с rawValue от тип String и опционален Date? за времето на последното отваряне. Всяко свойство е свързано с ключ UserDefaults, посочен като първи аргумент. Стойността по подразбиране се използва, ако ключът лилсва в хранилището при първото пускане.

Наблюдение на промените на Store

Едно от ключовите предимства на @AppStorage — автоматичното наблюдение на промените на UserDefaults от всякакъв източник. Ако App Extension или Widget промени стойност, @AppStorage в родителското приложение получава уведомление и прерисува View. Това се постига чрез механизма KVO (Key-Value Observing), който @AppStorage автоматично настроява на UserDefaults.didChangeNotification.

На практика това означава, че ако потребителят промени настройка в Widget (напр. включи тъмна тема), приложението незабавно поема тази промяна. Подобно работи синхронизацията между основното приложение и Share Extension, Watch App или Today Widget. Разработчикът няма нужда да пише код за межпроцесовен обмен на данни — @AppStorage прави това автоматично.

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("Тъмният режим е променен на \(newValue)")
                }
        }
    }
}

Toggle е свързан с $isDarkMode чрез @AppStorage. При преключване стойността се запазва автоматично в UserDefaults под ключа „isDarkMode”. Модификаторът .onChange позволява изпълняване на странично действие при промяна — например, изпращане на аналитика или актуализиране на UI на други екрани. Ако Widget промени същия ключ, @AppStorage ще вика също и onChange, осигурявайки непротиворечивост на състоянието.

Примери за код @AppStorage

Нека разгледаме пълен екран настройки на приложението, който използва @AppStorage за съхранение на всички конфигурации. Формата съдържа секции с различни типове настройки: текстови полета, прекълючватели, броячи — всички стойности се запазват автоматично в 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("Профил")) {
                    TextField("Display name", text: $displayName)
                }

                Section(header: Text("Настройки")) {
                    Toggle("Enable notifications",
                           isOn: $notificationsEnabled)
                    Stepper("Max results: \(maxResults)",
                           value: $maxResults,
                           in: 10...100,
                           step: 5)
                }

                Section {
                    Button("Нулиране на настройките") {
                        UserDefaults.standard.removePersistentDomain(
                            forName: Bundle.main.bundleIdentifier!)
                    }
                    .tint(.red)
                }
            }
            .navigationTitle("Settings")
        }
    }
}

Формата съдържа четири @AppStorage свойства от различни типове: String за име, Bool за уведомления, Int за броя резултати и String за избрания раздел. Всички контроли са свързани с свойствата чрез Binding ($displayName, $notificationsEnabled и др.). Бутонът „Reset settings” нулира всички UserDefaults, изтривайки домейна на приложението — след това @AppStorage автоматично се връща към стойностите по подразбиране.

Синхронизация @AppStorage с 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 = "система"

    @AppStorage("widgetColor", store: UserDefaults(suiteName: "group.com.example.app")!)
    var widgetColor: String = "синьо"

    var body: some View {
        Form {
            Picker("Widget theme", selection: $widgetTheme) {
                Text("Система").tag("system")
                Text("Светло").tag("светло")
                Text("Тъмно").tag("тъмно")
            }
            Picker("Accent color", selection: $widgetColor) {
                Text("Синьо").tag("blue")
                Text("Зелено").tag("зелено")
                Text("Червено").tag("червено")
            }
        }
    }
}

За App Group (споделено хранилище между приложение и разширения) @AppStorage приема параметър store: UserDefaults(suiteName:). Стойностите се запазват в общ контейнер, достъпен за основното приложение, Widget, Watch App и други разширения от същата група. Widget може да чете тези настройки, а при промяна в приложението Widget се актуализира автоматично чрез механизма за наблюдение на UserDefaults.

Често задавани въпроси

Каква е разликата между @AppStorage и @State?

@State съхранява стойността само в паметта и се нулира при рестартиране на приложението. @AppStorage запазва стойността в UserDefaults и гя възстановява при следващото пускане. Използвайте @State за временни данни на екрана, @AppStorage — за настройки, които трябва да преживеят рестартиране.

Може ли да се използва @AppStorage с Enum?

Да, ако Enum имплементира протокол RawRepresentable с rawValue от тип String или Int. Пример: @AppStorage("theme") var theme: AppTheme = .system. SwiftUI автоматично сериализира enum чрез rawValue и го възстановява при зареждане.

Как да изчистия всички @AppStorage стойности?

Извикайте UserDefaults.standard.removePersistentDomain(forName: Bundle.main.bundleIdentifier!) за стандартното хранилище или removeObject(forKey:) за конкретен ключ. След изчистване всички @AppStorage свойства се връщат към стойностите по подразбиране, посочени в декларацията.

Работи ли @AppStorage с App Extensions?

Да, за синхронизация между приложение и разширения използвайте App Group: @AppStorage("key", store: UserDefaults(suiteName: "group.com.example.app")!). Widget, Share Extension и Watch App могат да четат и пишат в същия UserDefaults, а промените се проследяват автоматично.

Какъв обем данни може да се съхранява в @AppStorage?

@AppStorage използва UserDefaults, който е предназначен за малки обеми от данни: настройки, токени, броячи. Препоръчаното ограничение е до 100 KB на приложение. За структурирани или големи данни (масиви от обекти, медийни файлове) използвайте SwiftData, Core Data или файловата система.

Резюме

  • @AppStorage — property wrapper за реактивно съхранение на настройки в UserDefaults
  • Автоматично запазване и автоматично обновяване на UI при промяна на стойността от всякаква места
  • Поддържа String, Int, Double, Bool, Data, URL и RawRepresentable enums
  • Стойност по подразбиране се задава в декларацията и се възстановява при първото пускане
  • App Group позволява синхронизация на @AppStorage между приложение и разширения
  • UserDefaults е подходящ само за малки обеми от данни — до 100 KB
  • Използвайте @AppStorage за потребителски настройки, @State — за временни състояния на екрана

Ще разработим мобилно приложение под ключ

IT Sectr създава iOS и Android приложения за стартъпи и бизнеси от 2017 г. Ще ви консултираме и ще предложим най-доброто решение.

Обсъдете проекта

Прочетете също