NSUserDefaults е хранилище за данни ключ-стойност в iOS, watchOS, tvOS и macOS, предназначено за запазване на настройки и конфигурации на приложението. Данните се съхраняват в plist файл в пясъчника на приложението и автоматично се синхронизират с iCloud чрез NSUbiquitousKeyValueStore. Според официалната документация Apple Developer, 2025, NSUserDefaults поддържа съхранение на примитивни типове: String, Int, Bool, Float, Double, Data, Date, Array и Dictionary. Класът е преименуван на UserDefaults от Swift 3 нататък, но неговото Objective-C име NSUserDefaults остава широко използвано в кодовата база и документацията на Apple.
Основни моменти
NSUserDefaults (UserDefaults в Swift) е вграден механизъм на Apple за съхранение на двойки ключ-стойност в plist формат. Той е достъпен на всички платформи на Apple: iOS, iPadOS, watchOS, tvOS и macOS. Основно предназначение — запазване на потребителски предпочитания, състояние на интерфейса, флагове за първо стартиране, избрани опции и други прости данни, които оцеляват при рестартиране на приложението.
Всяко iOS приложение има изолиран пясъчник и NSUserDefaults се съхранява в директорията Library/Preferences вътре в този пясъчник във файл с име Bundle Identifier. plist файлът съдържа двойки ключ-стойност, където ключът е низ, а стойността е един от поддържаните типове. Размерът на файла не е ограничен, но Apple препоръчва съхраняване само на настройки в UserDefaults, а не на големи обеми данни.
От iOS 8, NSUserDefaults поддържа App Groups — споделено хранилище между приложения на един и същ разработчик и техните разширения (widgets, watchOS придружаващи приложения). За това се използва инициализаторът init?(suiteName:) с идентификатор на App Group. Това позволява например на widget на 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 файла в RAM паметта под формата на Dictionary. Всички последващи четения се извършват от паметта. Записът също първо се извършва в паметта, а синхронизацията на диска се случва периодично на заден фон.
Операциите за запис използват метода 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". Данните, записани в този екземпляр, са достъпни от основното приложение, widget-а, 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.
Нека разгледаме практически пример: запазване и възстановяване на състоянието на onboarding (екрани за добре дошли) в iOS приложение с помощта на NSUserDefaults. При първото стартиране потребителят вижда onboarding екраните, след преминаването им флаг се запазва в UserDefaults. При следващи стартирания onboarding се пропуска. За SwiftUI се използва @AppStorage, за UIKit — директен достъп до UserDefaults.standard.
Нека създадем мениджър OnboardingManager, който капсулира работата с UserDefaults за съхранение на статуса на onboarding. Мениджърът предоставя свойството isOnboardingCompleted за проверка на състоянието и метода markOnboardingCompleted за задаване на флага. Ключът за съхранение е изнесен в константа за предотвратяване на правописни грешки. За unit тестване, мениджърът използва протокола 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. Обектът се сериализира в Data чрез JSONEncoder, запазва се чрез set(_:forKey:), и при четене се десериализира от Data обратно в обект чрез JSONDecoder. Този подход позволява съхраняване на сложни структури в 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 създава iOS и Android приложения за стартъпи и бизнеси от 2017 г. Ще ви консултираме и ще предложим най-доброто решение.
Прочетете също