@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?), која омогућава разликовање „није постављено” и „празна вредност”.

За чување прилагођених типова у складу са протоколом 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. године. Саветоваћемо вас и предложити најбоље решење.

Разговарајте о пројекту

Прочитајте такође