NSUserDefaults یک فروشگاه داده کلید-مقدار در iOS، watchOS، tvOS و macOS است که برای ذخیره تنظیمات و پیکربندی برنامه طراحی شده است. دادهها در فایل plist در sandbox برنامه ذخیره شده و به طور خودکار از طریق NSUbiquitousKeyValueStore با iCloud همگامسازی میشوند. طبق مستندات رسمی Apple Developer, 2025، NSUserDefaults از ذخیره انواع اولیه پشتیبانی میکند: String، Int، Bool، Float، Double، Data، Date، Array و Dictionary. این کلاس از Swift 3 به UserDefaults تغییر نام داده است، اما نام Objective-C آن یعنی NSUserDefaults همچنان در پایگاه کد و مستندات Apple به طور گسترده استفاده میشود.
نکات اصلی
NSUserDefaults (UserDefaults در Swift) — یک مکانیسم داخلی Apple برای ذخیره جفتهای کلید-مقدار در قالب plist است. این مکانیسم در تمام پلتفرمهای Apple در دسترس است: iOS، iPadOS، watchOS، tvOS و macOS. هدف اصلی — ذخیره ترجیحات کاربر، وضعیت رابط، پرچمهای راهاندازی اولیه، گزینههای انتخاب شده و سایر دادههای ساده که پس از راهاندازی مجدد برنامه باقی میمانند.
هر برنامه iOS یک sandbox ایزوله دارد و NSUserDefaults در پوشه Library/Preferences داخل این sandbox در فایلی با نام Bundle Identifier ذخیره میشود. فایل plist شامل جفتهای کلید-مقدار است که کلید یک رشته و مقدار یکی از انواع پشتیبانی شده است. اندازه فایل محدود نیست، اما Apple توصیه میکند فقط تنظیمات را در UserDefaults ذخیره کنید، نه حجم زیادی از داده.
از iOS 8، NSUserDefaults از App Groups پشتیبانی میکند — ذخیرهسازی مشترک بین برنامههای یک توسعهدهنده و افزونههای آنها (ویجتها، برنامههای همراه watchOS). برای این کار از مقداردهنده اولیه init?(suiteName:) با شناسه App Group استفاده میشود. این به ویجت در صفحه Today اجازه میدهد تنظیمات را از برنامه اصلی بدون تکرار منطق ذخیرهسازی بخواند.
از نظر فیزیکی، NSUserDefaults در فایل plist باینری در مسیر ذخیره میشود: {Sandbox}/Library/Preferences/com.example.myapp.plist. فایل از فرمت plist باینری (NSPropertyListBinaryFormat_v1_0) برای فشردگی و سرعت خواندن استفاده میکند. در macOS، فایل میتواند برای سازگاری در قالب XML باشد. بر خلاف SharedPreferences در Android، فایلهای plist UserDefaults میتوانند ساختارهای تودرتو را از طریق Dictionary و Array شامل شوند.
فایلهای NSUserDefaults به طور پیشفرض رمزگذاری نمیشوند. دادهها به صورت آشکار ذخیره میشوند و با دسترسی فیزیکی به دستگاه یا از طریق پشتیبانگیری قابل خواندن هستند. برای ذخیره دادههای حساس (رمزهای عبور، توکنها، کلیدهای رمزگذاری)، Apple به طور قطعی استفاده از Keychain را توصیه میکند که به طور خودکار دادهها را در سطح سیستم عامل رمزگذاری میکند.
NSUserDefaults بر اساس اصل ذخیره موقت در حافظه با همگامسازی دورهای روی دیسک کار میکند. در اولین مراجعه به نمونه استاندارد UserDefaults.standard، سیستم فایل plist را به صورت Dictionary در حافظه RAM بارگذاری میکند. تمام خوانشهای بعدی از حافظه انجام میشوند. نوشتن نیز ابتدا در حافظه انجام میشود و همگامسازی روی دیسک به صورت دورهای در پسزمینه رخ میدهد.
عملیات نوشتن از متد set(_:forKey:) استفاده میکند که مقدار اختیاری از نوع Any? را میپذیرد. مقدار میتواند nil باشد — برای حذف کلید. برای نوشتن فوری روی دیسک قبلاً از متد synchronize() استفاده میشد، اما از iOS 7 و OS X 10.9 دیگر نیازی به آن نیست — سیستم به طور خودکار دادهها را در فواصل منظم همگامسازی میکند. Apple رسماً اعلام کرده که synchronize() در مستندات خود زائد است.
NSUserDefaults از سیستم ثبتها (دامنهها) برای سازماندهی جستجوی مقادیر استفاده میکند. هنگامی که برنامه مقدار را با کلید درخواست میکند، UserDefaults به ترتیب دامنهها را به ترتیب مشخص بررسی میکند: ابتدا NSArgumentDomain (آرگومانهای خط فرمان)، سپس دامنه برنامه (Application)، سپس NSGlobalDomain (تنظیمات سیستم)، سپس دامنههای خاص زبان و در نهایت NSRegistrationDomain (مقادیر پیشفرض ثبت شده از طریق register(defaults:)).
import Foundation
// نمونه استاندارد UserDefaults
let defaults = UserDefaults.standard
// نوشتن مقادیر
defaults.set("آنا پترووا", forKey: "username")
defaults.set(28, forKey: "age")
defaults.set(true, forKey: "isLoggedIn")
// ثبت مقادیر پیشفرض
defaults.register(defaults: [
"theme": "system",
"fontSize": 14
])
// خواندن با برگرداندن مقدار پیشفرض
let theme = defaults.string(forKey: "theme") ?? "system"
let fontSize = defaults.integer(forKey: "fontSize")
دامنه NSRegistrationDomain یک دامنه برنامهای است که فقط در حافظه RAM وجود دارد و روی دیسک ذخیره نمیشود. این دامنه برای تنظیم مقادیر پیشفرض استفاده میشود که تا زمانی که برنامه مقدار خود را در دامنه برنامه ننویسد فعال هستند. این امکان ایجاد یک نقطه پیکربندی واحد برای تنظیمات پیشفرض را فراهم میکند که میتوان در مرحله توسعه به صورت متمرکز تغییر داد.
NSUserDefaults مجموعهای از متدهای تایپشده برای خواندن و نوشتن دادهها ارائه میدهد: string(forKey:)، integer(forKey:)، bool(forKey:)، float(forKey:)، double(forKey:)، data(forKey:)، array(forKey:)، dictionary(forKey:) و object(forKey:). هر متد خواندن یک متد نوشتن متناظر set(_:forKey:) با تعیین خودکار نوع مقدار ذخیره شده دارد. نسخه Swift UserDefaults از تایپسازی دقیق استفاده میکند، اما نسخه Objective-C id را میپذیرد و برمیگرداند.
| متد خواندن (Swift) | نوع داده | مقدار پیشفرض |
|---|---|---|
| string(forKey:) | String? | nil |
| integer(forKey:) | Int | 0 |
| bool(forKey:) | Bool | false |
| float(forKey:) | Float | 0.0 |
| double(forKey:) | Double | 0.0 |
| data(forKey:) | Data? | nil |
متد synchronize() در NSUserDefaults تمام تغییرات را از حافظه به زور روی دیسک مینویسد. در نسخههای اولیه iOS، این متد باید پس از هر نوشتن برای تضمین ذخیره داده فراخوانی میشد. از iOS 7، سیستم به طور خودکار UserDefaults را در پسزمینه همگامسازی میکند و Apple رسماً synchronize() را زائد اعلام کرده است. فراخوانی این متد خطایی ایجاد نمیکند، اما هیچ تضمینی برای ذخیره اضافی ارائه نمیدهد.
برای نظارت بر تغییرات، NSUserDefaults اعلان UserDefaults.didChangeNotification و متد مشاهده KVO addObserver(_:forKeyPath:options:context:) را ارائه میدهد. در SwiftUI، Property Wrapper @AppStorage در دسترس است که به طور خودکار مقدار UserDefaults را با بهروزرسانی UI همگامسازی میکند. @AppStorage از همان انواع UserDefaults پشتیبانی میکند و روش ترجیحی کار با تنظیمات در برنامههای SwiftUI است.
// مشاهده تغییرات از طریق KVO
class SettingsViewModel: NSObject {
override func observeValue(
forKeyPath keyPath: String?,
of object: Any?,
change: [NSKeyValueChangeKey: Any]?,
context: UnsafeMutableRawPointer?
) {
guard let keyPath else { return }
print("کلید تغییر کرد: \(keyPath)")
}
}
// SwiftUI - AppStorage
struct SettingsView: View {
@AppStorage("theme") private var theme: String = "system"
var body: some View {
Picker("تم", selection: $theme) {
Text("سیستمی").tag("system")
Text("روشن").tag("light")
Text("تاریک").tag("dark")
}
}
}
برای کار با App Groups (ذخیرهسازی مشترک بین برنامه و افزونهها) از مقداردهنده اولیه UserDefaults(suiteName:) با شناسه App Group استفاده میشود. به عنوان مثال، "group.com.example.myapp". دادههای نوشته شده در این نمونه از برنامه اصلی، ویجت، برنامه همراه watchOS و سایر افزونههای متعلق به یک App Group قابل دسترسی هستند. هر نمونه suite در یک فایل plist جداگانه ذخیره میشود.
با وجود راحتی و سادگی، NSUserDefaults یک فروشگاه جهانی برای همه انواع داده در iOS نیست. بسته به حجم، حساسیت و الزامات امنیتی، Apple چندین جایگزین ارائه میدهد که هر کدام برای سناریوی استفاده خاص بهینه شدهاند.
| راهکار | چه زمانی استفاده شود | محدودیتها |
|---|---|---|
| NSUserDefaults | تنظیمات رابط و پیکربندی | برای دادههای حجیم و اسرار مناسب نیست |
| Keychain | رمزهای عبور، توکنها، کلیدهای رمزگذاری | استفاده پیچیدهتر، کندتر |
| CoreData | دادههای ساختاریافته با روابط | برای 10-20 تنظیم بیش از حد |
| FileManager | اسناد، تصاویر، دادههای باینری | نیاز به مدیریت دستی فایلها |
| CloudKit | همگامسازی ابری بین دستگاهها | نیاز به حساب iCloud و اتصال شبکه |
Keychain — یک فروشگاه محافظت شده Apple برای دادههای محرمانه است. بر خلاف NSUserDefaults، تمام دادهها در Keychain در سطح سیستم عامل با استفاده از رمزگذاری سختافزاری Secure Enclave در دستگاههای سازگار رمزگذاری میشوند. Keychain به طور خودکار همراه با دستگاه قفل و باز میشود و از اشتراک دسترسی بین برنامههای یک توسعهدهنده از طریق Keychain Access Groups پشتیبانی میکند.
عیب اصلی Keychain — پیچیدگی API است. برای ذخیره ساده یک رشته، باید درخواست SecItemAdd با مشخص کردن ویژگیها ایجاد کنید: کلاس (kSecClassGenericPassword)، سرویس (kSecAttrService)، حساب (kSecAttrAccount) و خود داده (kSecValueData). برای سادهسازی کار با Keychain، بستههای شخص ثالثی مانند KeychainAccess و SwiftKeychainWrapper وجود دارند که رابط راحت کلید-مقدار مشابه UserDefaults ارائه میدهند.
بیایید یک مثال عملی را بررسی کنیم: ذخیره و بازیابی وضعیت راهنما (صفحات خوشآمدگویی) در یک برنامه iOS با استفاده از NSUserDefaults. در اولین راهاندازی، کاربر صفحات راهنما را میبیند، پس از عبور از آنها، پرچم در UserDefaults ذخیره میشود. در راهاندازیهای بعدی، راهنما رد میشود. برای SwiftUI از @AppStorage استفاده میشود، برای UIKit — دسترسی مستقیم به UserDefaults.standard.
مدیری به نام OnboardingManager ایجاد میکنیم که کار با UserDefaults را برای ذخیره وضعیت راهنما کپسوله میکند. مدیر ویژگی isOnboardingCompleted برای بررسی وضعیت و متد markOnboardingCompleted برای تنظیم پرچم ارائه میدهد. کلید ذخیرهسازی برای جلوگیری از اشتباهات تایپی در یک ثابت قرار گرفته است. برای تست واحد، مدیر از پروتکل UserDefaultsProtocol استفاده میکند که امکان جایگزینی فروشگاه واقعی با MockUserDefaults را فراهم میکند.
class OnboardingManager {
private let defaults: UserDefaults
private let hasSeenKey = "has_seen_onboarding"
init(defaults: UserDefaults = .standard) {
self.defaults = defaults
}
var isOnboardingCompleted: Bool {
defaults.bool(forKey: hasSeenKey)
}
func markOnboardingCompleted() {
defaults.set(true, forKey: hasSeenKey)
}
func resetOnboarding() {
defaults.removeObject(forKey: hasSeenKey)
}
}
// استفاده در برنامه
let onboardingManager = OnboardingManager()
if !onboardingManager.isOnboardingCompleted {
showOnboarding()
} else {
showMainScreen()
}
برای ذخیره تنظیمات پیچیدهتر، مانند یک شیء Profile ساختاریافته، استفاده از پروتکل Codable و JSONEncoder/JSONDecoder توصیه میشود. شیء از طریق JSONEncoder به Data سریالایز میشود، از طریق set(_:forKey:) ذخیره میشود و هنگام خواندن از طریق JSONDecoder از Data دوباره به شیء دیسریالایز میشود. این رویکرد امکان ذخیره ساختارهای پیچیده در UserDefaults را بدون از دست دادن ایمنی نوع فراهم میکند.
struct UserProfile: Codable {
let name: String
let age: Int
let preferences: [String: String]
}
extension UserDefaults {
func save<T: Codable>(_ value: T, forKey key: String) {
if let data = try? JSONEncoder().encode(value) {
set(data, forKey: key)
}
}
func load<T: Codable>(_ type: T.Type, forKey key: String) -> T? {
guard let data = data(forKey: key) else { return nil }
return try? JSONDecoder().decode(type, from: data)
}
}
// استفاده
let profile = UserProfile(name: "آنا", age: 28, preferences: ["theme": "dark"])
UserDefaults.standard.save(profile, forKey: "user_profile")
let loaded = UserDefaults.standard.load(UserProfile.self, forKey: "user_profile")
مهم است به خاطر داشته باشید که NSUserDefaults برای ذخیره حجم زیادی از داده طراحی نشده است. Apple توصیه میکند اندازه دادههای ذخیره شده را به چند ده کیلوبایت محدود کنید. برای ذخیره اشیاء بزرگ (تصاویر، اسناد، مدلهای سریالایز شده) باید از FileManager با پوشه Documents یا CoreData استفاده کنید. علاوه بر این، UserDefaults از نسخهگذاری طرح داده پشتیبانی نمیکند — هنگام تغییر ساختار مدل Codable، دادههای قدیمی ممکن است دیسریالایز نشوند و این باید در کد برنامه مدیریت شود.
سوالات متداول
هر دو فروشگاه کلید-مقدار هستند، اما NSUserDefaults انواع بیشتری (Data, Date, Array, Dictionary) را پشتیبانی میکند و به طور خودکار با iCloud همگام میشود. SharedPreferences دادهها را در XML ذخیره میکند، NSUserDefaults — در قالب plist. NSUserDefaults دارای سیستم دامنه با جستجوی آبشاری است، SharedPreferences از ساختار تخت ساده با نام فایلها استفاده میکند.
خیر، NSUserDefaults دادهها را به صورت آشکار بدون رمزگذاری ذخیره میکند. برای رمزهای عبور، توکنها و کلیدهای رمزگذاری از Keychain استفاده کنید که دادهها را در سطح Secure Enclave رمزگذاری میکند. Keychain همچنین از ویژگیهای دسترسی مانند احراز هویت بیومتریک (Face ID / Touch ID) قبل از خواندن رمز پشتیبانی میکند.
برای همگامسازی بین دستگاههای یک کاربر از NSUbiquitousKeyValueStore — فروشگاه کلید-مقدار ابری iCloud استفاده کنید. دادههای نوشته شده در این سرویس در یک دستگاه به طور خودکار در همه دستگاههای دیگر همان حساب iCloud ظاهر میشوند. حداکثر حجم — 1 MB برای هر برنامه، 1024 کلید.
برای حذف همه دادهها، متد removePersistentDomain(forName:) را با Bundle Identifier برنامه فراخوانی کنید. برای حذف مقادیر تکی از removeObject(forKey:) استفاده کنید. برای بازنشانی کامل تنظیمات برنامه: UserDefaults.standard.removePersistentDomain(forName: Bundle.main.bundleIdentifier!). همه حذفها فوراً در کش حافظه اعمال میشوند.
Apple محدودیت سختی برای اندازه NSUserDefaults تعیین نمیکند، اما توصیه میشود از 100 KB برای حجم کل تمام دادههای ذخیره شده تجاوز نکنید. برای حجمهای بزرگ از CoreData یا FileManager استفاده کنید. هنگام ذخیره بیش از 1 MB داده، عملکرد خواندن در راهاندازی برنامه میتواند به طور محسوسی کاهش یابد.
خلاصه
ما یک اپلیکیشن موبایل به صورت کلید در دست توسعه خواهیم داد
IT Sectr از سال 2017 برنامههای iOS و Android را برای استارتاپها و کسبوکارها ایجاد میکند. ما به شما مشاوره میدهیم و بهترین راهحل را پیشنهاد خواهیم کرد.
همچنین بخوانید