@AppStorage در SwiftUI — property wrapper برای کار با UserDefaults که به طور خودکار مقدار را با UI همگامسازی میکند. هنگام تغییر ویژگی اعلامشده از طریق @AppStorage، مقدار جدید فوراً در UserDefaults ذخیره میشود و هنگامی که UserDefaults از خارج — توسط ویجت یا افزونه — تغییر میکند، View به طور خودکار دوباره ترسیم میشود. به گفته Apple Developer Documentation (2025)، @AppStorage از String، Int، Double، Bool، Data، URL و نسخههای اختیاری آنها پشتیبانی میکند و ذخیرهسازی واکنشی تنظیمات کاربر را بدون کد نظارت دستی فراهم میکند.
نکات اصلی
@AppStorage یک property wrapper است که توسط Apple در iOS 14 معرفی شده و ویژگی View را به یک کلید در UserDefaults متصل میکند. هنگام خواندن ویژگی، SwiftUI مقدار را از UserDefaults بر اساس کلید مشخصشده بارگیری میکند. هنگام نوشتن — مقدار جدید را ذخیره کرده و به View برای ترسیم مجدد اطلاع میدهد.
پیش از ظهور @AppStorage، توسعهدهندگان مجبور بودند به صورت دستی UserDefaults را در onAppear بخوانند، در اعلان UserDefaults.didChangeNotification مشترک شوند و @State را در تغییرات بهروزرسانی کنند. @AppStorage کل چرخه را خودکار میکند: اعلام در یک خط جایگزین ۱۵–۲۰ خط کادرساز (boilerplate) میشود. علاوه بر این، @AppStorage همگامسازی دوطرفه را فراهم میکند — اگر مقدار UserDefaults از فرآیند دیگری (مثلاً App Extension یا Widget) تغییر کند، View همچنان بهروزرسانی دریافت میکند.
از نظر معماری، @AppStorage به عنوان DynamicProperty پیادهسازی شده است که به SwiftUI اجازه میدهد وابستگیها را ردیابی کرده و View را هنگام تغییر مقدار مشاهدهشده دوباره ترسیم کند. این آن را برای ذخیره تنظیمات کاربر ایدهآل میکند: زبان رابط، فعال/غیرفعال کردن ویژگیها، آخرین تب انتخابشده، نام کاربری.
اگرچه @AppStorage در پسزمینه از UserDefaults استفاده میکند، رویکردهای کار با ذخیرهسازی تفاوت اساسی دارند. UserDefaults یک API سطح پایین است که نیاز به مدیریت دستی خواندن، نوشتن و اعلانهای تغییر دارد. @AppStorage یک انتزاع SwiftUI است که رفتار واکنشی را به صورت آماده ارائه میدهد.
UserDefaults برای عملیات یکباره مناسب است: بارگیری تنظیمات در شروع برنامه، نوشتن تحلیل، کش کردن توکنها. @AppStorage — برای تنظیماتی که باید UI را به صورت واکنشی بهروزرسانی کنند: سوئیچهای تم، انتخاب زبان، ذخیره وضعیت رابط. استفاده مستقیم از UserDefaults درون View یک ضدالگو است، زیرا View بدون اشتراک اضافی از تغییرات مطلع نمیشود.
| پارامتر | @AppStorage | UserDefaults |
|---|---|---|
| واکنشپذیری | خودکار | نیاز به اشتراک در اعلانها |
| کادرساز | ۱ خط به ازای هر ویژگی | ۱۵–۲۰ خط به ازای هر ویژگی |
| انواع | 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 سریالسازی/دسریالسازی میکند.
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 مشخصشده در اولین آرگومان متصل است. مقدار پیشفرض زمانی استفاده میشود که کلید در اولین راهاندازی در ذخیرهسازی وجود نداشته باشد.
یکی از مزایای کلیدی @AppStorage — نظارت خودکار بر تغییرات UserDefaults از هر منبعی است. اگر App Extension یا Widget مقداری را تغییر دهد، @AppStorage در برنامه والد اعلان دریافت کرده و View را دوباره ترسیم میکند. این از طریق مکانیسم KVO (Key-Value Observing) حاصل میشود که @AppStorage به طور خودکار بر روی UserDefaults.didChangeNotification تنظیم میکند.
در عمل این بدان معناست که اگر کاربر تنظیماتی را در Widget تغییر دهد (مثلاً تم تیره را فعال کند)، برنامه بلافاصله این تغییر را دریافت میکند. به طور مشابه، همگامسازی بین برنامه اصلی و Share Extension، Watch App یا Today Widget کار میکند. توسعهدهنده نیازی به نوشتن کد برای تبادل داده بین فرآیندی ندارد — @AppStorage این کار را به طور خودکار انجام میدهد.
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 از طریق @AppStorage به $isDarkMode متصل است. هنگام تغییر، مقدار به طور خودکار در UserDefaults تحت کلید «isDarkMode» ذخیره میشود. اصلاحکننده .onChange امکان انجام یک اقدام جانبی در هنگام تغییر را فراهم میکند — مثلاً ارسال تحلیل یا بهروزرسانی UI سایر صفحهها. اگر Widget همان کلید را تغییر دهد، @AppStorage نیز onChange را فراخوانی میکند و ثبات وضعیت را تضمین میکند.
بیایید یک صفحه تنظیمات کامل برنامه را بررسی کنیم که از @AppStorage برای ذخیره تمام پیکربندیها استفاده میکند. فرم شامل بخشهایی با انواع مختلف تنظیمات است: فیلدهای متنی، سوئیچها، شمارندهها — همه مقادیر به طور خودکار در 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("پروفایل")) {
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 به طور خودکار به مقادیر پیشفرض بازمیگردد.
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 بهروزرسانی میشود.
سوالات متداول
@State مقدار را فقط در حافظه ذخیره میکند و با راهاندازی مجدد برنامه بازنشانی میشود. @AppStorage مقدار را در UserDefaults ذخیره میکند و در راهاندازی بعدی بازیابی میکند. از @State برای دادههای موقت صفحه و از @AppStorage برای تنظیماتی که باید راهاندازی مجدد را تحمل کنند استفاده کنید.
بله، اگر Enum پروتکل RawRepresentable را با rawValue از نوع String یا Int پیادهسازی کند. مثال: @AppStorage("theme") var theme: AppTheme = .system. SwiftUI به طور خودکار enum را از طریق rawValue سریالسازی کرده و در بارگیری بازیابی میکند.
UserDefaults.standard.removePersistentDomain(forName: Bundle.main.bundleIdentifier!) را برای ذخیرهسازی استاندارد یا removeObject(forKey:) را برای کلید خاص فراخوانی کنید. پس از پاکسازی، تمام ویژگیهای @AppStorage به مقادیر پیشفرض مشخصشده در اعلام بازمیگردند.
بله، برای همگامسازی بین برنامه و افزونهها از App Group استفاده کنید: @AppStorage("key", store: UserDefaults(suiteName: "group.com.example.app")!). Widget، Share Extension و Watch App میتوانند در همان UserDefaults بخوانند و بنویسند و تغییرات به طور خودکار ردیابی میشوند.
@AppStorage از UserDefaults استفاده میکند که برای حجم کم داده طراحی شده است: تنظیمات، توکنها، شمارندهها. حد توصیهشده — تا ۱۰۰ کیلوبایت به ازای هر برنامه. برای دادههای ساختاریافته یا حجیم (آرایههای اشیاء، فایلهای چندرسانهای) از SwiftData، Core Data یا سیستم فایل استفاده کنید.
خلاصه
ما یک اپلیکیشن موبایل به صورت کلید در دست توسعه خواهیم داد
IT Sectr از سال 2017 برنامههای iOS و Android را برای استارتاپها و کسبوکارها ایجاد میکند. ما به شما مشاوره میدهیم و بهترین راهحل را پیشنهاد خواهیم کرد.
همچنین بخوانید