@AppStorage di SwiftUI — property wrapper untuk bekerja dengan UserDefaults, yang secara otomatis menyinkronkan nilai dengan UI. Saat properti yang dideklarasikan melalui @AppStorage berubah, nilai baru segera disimpan di UserDefaults, dan ketika UserDefaults berubah dari luar — oleh widget atau ekstensi — View secara otomatis digambar ulang. Menurut Apple Developer Documentation (2025), @AppStorage mendukung String, Int, Double, Bool, Data, URL dan versi opsionalnya, menyediakan penyimpanan reaktif pengaturan pengguna tanpa kode observasi manual.
Poin Utama
@AppStorage adalah property wrapper yang diperkenalkan oleh Apple di iOS 14, yang menghubungkan properti View dengan kunci di UserDefaults. Saat membaca properti, SwiftUI memuat nilai dari UserDefaults berdasarkan kunci yang ditentukan. Saat menulis — menyimpan nilai baru dan memberi tahu View untuk menggambar ulang.
Sebelum munculnya @AppStorage, pengembang harus membaca UserDefaults secara manual di onAppear, berlangganan pemberitahuan UserDefaults.didChangeNotification dan memperbarui @State saat ada perubahan. @AppStorage mengotomatiskan seluruh siklus: deklarasi satu baris menggantikan 15–20 baris kode boilerplate. Lebih dari itu, @AppStorage menyediakan sinkronisasi dua arah — jika nilai UserDefaults diubah dari proses lain (misalnya, App Extension atau Widget), View tetap akan menerima pembaruan.
Secara arsitektur, @AppStorage diimplementasikan sebagai DynamicProperty, yang memungkinkan SwiftUI melacak dependensi dan menggambar ulang View saat nilai yang diamati berubah. Ini membuatnya ideal untuk menyimpan pengaturan pengguna: bahasa antarmuka, mengaktifkan/menonaktifkan fitur, tab terakhir yang dipilih, nama pengguna.
Meskipun @AppStorage menggunakan UserDefaults di bawah kap, pendekatan kerja dengan penyimpanan berbeda secara fundamental. UserDefaults adalah API tingkat rendah yang memerlukan pengelolaan manual pembacaan, penulisan, dan pemberitahuan perubahan. @AppStorage adalah abstraksi SwiftUI yang memberikan perilaku reaktif langsung dari kotak.
UserDefaults cocok untuk operasi sekali pakai: memuat pengaturan saat startup aplikasi, menulis analitik, menyimpan cache token. @AppStorage — untuk pengaturan yang harus memperbarui UI secara reaktif: sakelar tema, pemilihan bahasa, menyimpan status antarmuka. Penggunaan langsung UserDefaults di dalam View adalah antipattern, karena View tidak mengetahui perubahan tanpa langganan tambahan.
| Parameter | @AppStorage | UserDefaults |
|---|---|---|
| Reaktivitas | Otomatis | Memerlukan langganan pemberitahuan |
| Boilerplate | 1 baris per properti | 15–20 baris per properti |
| Tipe | String, Int, Double, Bool, Data, URL | Semua tipe + objek arsip |
| Tipe kustom | Melalui RawRepresentable | Melalui NSKeyedArchiver |
| App Extension | Sinkronisasi otomatis | Langganan manual |
Untuk pengaturan sederhana dengan UI reaktif, @AppStorage adalah pilihan yang lebih disukai. Untuk data kompleks (array, kamus, objek kustom) gunakan kombinasi UserDefaults dengan @State dan langganan manual perubahan, atau beralih ke SwiftData / Core Data untuk penyimpanan terstruktur.
@AppStorage mendukung tipe standar yang dapat diserialisasi langsung oleh UserDefaults: String, Int, Double, Bool, Data, URL. Untuk setiap tipe terdapat versi opsional (String?, Int?, Double?, Bool?, Data?, URL?), yang memungkinkan pembedaan antara “belum diatur” dan “nilai kosong”.
Untuk penyimpanan tipe kustom yang sesuai dengan protokol RawRepresentable, @AppStorage juga bekerja secara otomatis. Jika enum memiliki rawValue bertipe String atau Int, dapat digunakan langsung: @AppStorage("theme") var theme: AppTheme = .system. SwiftUI secara otomatis men-serialisasi/deserialisasi nilai melalui rawValue.
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("Dijalankan \(launchCount) kali")
}
}
}
Dalam contoh digunakan berbagai tipe @AppStorage: String dengan nilai default “Guest”, Int untuk penghitung startup, Bool untuk tema gelap, enum AppTheme dengan rawValue bertipe String dan Date? opsional untuk waktu terakhir dibuka. Setiap properti terikat pada kunci UserDefaults yang ditentukan sebagai argumen pertama. Nilai default digunakan jika kunci tidak ada di penyimpanan saat pertama kali dijalankan.
Salah satu keunggulan utama @AppStorage — pengamatan otomatis perubahan UserDefaults dari sumber mana pun. Jika App Extension atau Widget mengubah nilai, @AppStorage di aplikasi induk menerima pemberitahuan dan menggambar ulang View. Ini dicapai melalui mekanisme KVO (Key-Value Observing), yang secara otomatis diatur @AppStorage pada UserDefaults.didChangeNotification.
Dalam praktiknya, ini berarti jika pengguna mengubah pengaturan di Widget (misalnya, mengaktifkan tema gelap), aplikasi segera menangkap perubahan tersebut. Demikian pula sinkronisasi bekerja antara aplikasi utama dan Share Extension, Watch App atau Today Widget. Pengembang tidak perlu menulis kode untuk pertukaran data antar-proses — @AppStorage melakukannya secara otomatis.
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("Mode gelap diubah menjadi \(newValue)")
}
}
}
}
Toggle terhubung dengan $isDarkMode melalui @AppStorage. Saat diubah, nilai secara otomatis disimpan di UserDefaults di bawah kunci “isDarkMode”. Pengubah .onChange memungkinkan melakukan tindakan sampingan saat perubahan — misalnya, mengirim analitik atau memperbarui UI layar lain. Jika Widget mengubah kunci yang sama, @AppStorage juga akan memanggil onChange, memastikan konsistensi status.
Mari kita lihat layar pengaturan aplikasi lengkap yang menggunakan @AppStorage untuk menyimpan semua konfigurasi. Formulir berisi bagian dengan berbagai jenis pengaturan: bidang teks, sakelar, penghitung — semua nilai secara otomatis disimpan di UserDefaults.
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("Preferensi")) {
Toggle("Enable notifications",
isOn: $notificationsEnabled)
Stepper("Max results: \(maxResults)",
value: $maxResults,
in: 10...100,
step: 5)
}
Section {
Button("Reset pengaturan") {
UserDefaults.standard.removePersistentDomain(
forName: Bundle.main.bundleIdentifier!)
}
.tint(.red)
}
}
.navigationTitle("Settings")
}
}
}
Formulir berisi empat properti @AppStorage dari berbagai tipe: String untuk nama, Bool untuk pemberitahuan, Int untuk jumlah hasil dan String untuk tab yang dipilih. Semua kontrol terhubung ke properti melalui Binding ($displayName, $notificationsEnabled dll.). Tombol “Reset settings” mereset semua UserDefaults, menghapus domain aplikasi — setelah itu @AppStorage secara otomatis kembali ke nilai default.
struct SharedSettingsView: View {
let sharedDefaults = UserDefaults(suiteName: "group.com.example.app")
@AppStorage("widgetTheme", store: UserDefaults(suiteName: "group.com.example.app")!)
var widgetTheme: String = "sistem"
@AppStorage("widgetColor", store: UserDefaults(suiteName: "group.com.example.app")!)
var widgetColor: String = "biru"
var body: some View {
Form {
Picker("Widget theme", selection: $widgetTheme) {
Text("Sistem").tag("system")
Text("Terang").tag("terang")
Text("Gelap").tag("gelap")
}
Picker("Accent color", selection: $widgetColor) {
Text("Biru").tag("blue")
Text("Hijau").tag("hijau")
Text("Merah").tag("merah")
}
}
}
}
Untuk App Group (penyimpanan bersama antara aplikasi dan ekstensi) @AppStorage menerima parameter store: UserDefaults(suiteName:). Nilai disimpan dalam wadah bersama yang dapat diakses oleh aplikasi utama, Widget, Watch App dan ekstensi lain dari grup yang sama. Widget dapat membaca pengaturan ini, dan saat perubahan di aplikasi, Widget secara otomatis diperbarui melalui mekanisme observasi UserDefaults.
Pertanyaan yang Sering Diajukan
@State menyimpan nilai hanya di memori dan direset saat aplikasi dijalankan ulang. @AppStorage menyimpan nilai di UserDefaults dan mengembalikannya saat startup berikutnya. Gunakan @State untuk data sementara layar, @AppStorage — untuk pengaturan yang harus bertahan saat restart.
Ya, jika Enum mengimplementasikan protokol RawRepresentable dengan rawValue bertipe String atau Int. Contoh: @AppStorage("theme") var theme: AppTheme = .system. SwiftUI secara otomatis men-serialisasi enum melalui rawValue dan mengembalikannya saat dimuat.
Panggil UserDefaults.standard.removePersistentDomain(forName: Bundle.main.bundleIdentifier!) untuk penyimpanan standar atau removeObject(forKey:) untuk kunci tertentu. Setelah penghapusan, semua properti @AppStorage akan kembali ke nilai default yang ditentukan dalam deklarasi.
Ya, untuk sinkronisasi antara aplikasi dan ekstensi gunakan App Group: @AppStorage("key", store: UserDefaults(suiteName: "group.com.example.app")!). Widget, Share Extension dan Watch App dapat membaca dan menulis di UserDefaults yang sama, dan perubahan dilacak secara otomatis.
@AppStorage menggunakan UserDefaults, yang dirancang untuk volume kecil data: pengaturan, token, penghitung. Batas yang disarankan — hingga 100 KB per aplikasi. Untuk data terstruktur atau besar (array objek, file media) gunakan SwiftData, Core Data atau sistem file.
Ringkasan
Kami akan mengembangkan aplikasi seluler turnkey
IT Sectr membuat aplikasi iOS dan Android untuk startup dan bisnis sejak 2017. Kami akan memberi saran dan mengusulkan solusi terbaik.
Baca juga