NSUserDefaults este un stoc de date cheie-valoare în iOS, watchOS, tvOS și macOS, destinat salvării setărilor și configurațiilor aplicației. Datele sunt stocate într-un fișier plist în sandbox-ul aplicației și se sincronizează automat cu iCloud prin NSUbiquitousKeyValueStore. Conform documentației oficiale Apple Developer, 2025, NSUserDefaults suportă stocarea tipurilor primitive: String, Int, Bool, Float, Double, Data, Date, Array și Dictionary. Clasa a fost redenumită în UserDefaults începând cu Swift 3, dar numele său Objective-C NSUserDefaults rămâne larg utilizat în baza de cod și documentația Apple.
Puncte principale
NSUserDefaults (UserDefaults în Swift) este un mecanism încorporat Apple pentru stocarea perechilor cheie-valoare în format plist. Este disponibil pe toate platformele Apple: iOS, iPadOS, watchOS, tvOS și macOS. Scopul principal — salvarea preferințelor utilizatorului, stării interfeței, flag-urilor de first-launch, opțiunilor selectate și a altor date simple care supraviețuiesc repornirii aplicației.
Fiecare aplicație iOS are un sandbox izolat, iar NSUserDefaults este stocat în directorul Library/Preferences în interiorul acestui sandbox într-un fișier cu numele Bundle Identifier. Fișierul plist conține perechi cheie-valoare, unde cheia este un șir de caractere, iar valoarea este unul dintre tipurile suportate. Dimensiunea fișierului nu este limitată, dar Apple recomandă stocarea doar a setărilor în UserDefaults, nu a unor volume mari de date.
Începând cu iOS 8, NSUserDefaults suportă App Groups — stocare partajată între aplicațiile aceluiași dezvoltator și extensiile lor (widgeturi, aplicații companion watchOS). Pentru aceasta se utilizează inițializatorul init?(suiteName:) cu identificatorul App Group. Acest lucru permite, de exemplu, unui widget de pe ecranul Today să citească setări din aplicația principală fără a duplica logica de salvare.
Fizic, NSUserDefaults este stocat într-un fișier plist binar la calea: {Sandbox}/Library/Preferences/com.example.myapp.plist. Fișierul folosește formatul binar plist (NSPropertyListBinaryFormat_v1_0) pentru compactitate și viteză de citire. Pe macOS, fișierul poate fi în format XML pentru compatibilitate. Spre deosebire de SharedPreferences pe Android, fișierele plist UserDefaults pot conține structuri imbricate prin Dictionary și Array.
Fișierele NSUserDefaults nu sunt criptate implicit. Datele sunt stocate în formă deschisă și pot fi citite cu acces fizic la dispozitiv sau prin backup. Pentru stocarea datelor sensibile (parole, tokenuri, chei de criptare), Apple recomandă categoric utilizarea Keychain, care criptează automat datele la nivelul sistemului de operare.
NSUserDefaults funcționează pe principiul cache-ului în memorie cu sincronizare periodică pe disc. La prima accesare a instanței standard UserDefaults.standard, sistemul încarcă fișierul plist în memoria RAM sub formă de Dictionary. Toate citirile ulterioare se fac din memorie. Scrierea are loc, de asemenea, mai întâi în memorie, iar sincronizarea pe disc are loc periodic în fundal.
Operațiile de scriere folosesc metoda set(_:forKey:), care acceptă o valoare de tip opțional Any?. Valoarea poate fi nil — pentru ștergerea cheii. Pentru scrierea imediată pe disc se folosea anterior metoda synchronize(), dar începând cu iOS 7 și OS X 10.9 nu mai este necesară — sistemul sincronizează automat datele la intervale regulate. Apple a declarat oficial synchronize() ca fiind redundantă în documentația sa.
NSUserDefaults folosește un sistem de registre (domenii) pentru organizarea căutării valorilor. Când aplicația solicită o valoare după cheie, UserDefaults verifică succesiv domeniile într-o ordine specifică: mai întâi NSArgumentDomain (argumente din linia de comandă), apoi domeniul aplicației (Application), apoi NSGlobalDomain (setări sistem), apoi domeniile specifice limbii și în final NSRegistrationDomain (valorile implicite înregistrate prin register(defaults:)).
import Foundation
// Instanța standard UserDefaults
let defaults = UserDefaults.standard
// Scrierea valorilor
defaults.set("Ana Petrova", forKey: "username")
defaults.set(28, forKey: "age")
defaults.set(true, forKey: "isLoggedIn")
// Înregistrarea valorilor implicite
defaults.register(defaults: [
"theme": "system",
"fontSize": 14
])
// Citirea cu returnarea valorii implicite
let theme = defaults.string(forKey: "theme") ?? "system"
let fontSize = defaults.integer(forKey: "fontSize")
Domeniul NSRegistrationDomain este un domeniu software care există doar în memoria RAM și nu este salvat pe disc. Este utilizat pentru a stabili valori implicite care sunt active până când aplicația își scrie propria valoare în domeniul aplicației. Acest lucru permite crearea unui punct unic de configurare a setărilor implicite care pot fi modificate centralizat în faza de dezvoltare.
NSUserDefaults oferă un set de metode tipizate pentru citirea și scrierea datelor: string(forKey:), integer(forKey:), bool(forKey:), float(forKey:), double(forKey:), data(forKey:), array(forKey:), dictionary(forKey:) și object(forKey:). Fiecare metodă de citire are o metodă de scriere corespunzătoare set(_:forKey:) cu determinare automată a tipului valorii stocate. Versiunea Swift a UserDefaults utilizează tipizare strictă, dar versiunea Objective-C acceptă și returnează id.
| Metodă de citire (Swift) | Tip de date | Valoare implicită |
|---|---|---|
| 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 |
Metoda synchronize() în NSUserDefaults forțează scrierea tuturor modificărilor din memorie pe disc. În versiunile timpurii de iOS, această metodă trebuia apelată după fiecare scriere pentru garantarea salvării datelor. Începând cu iOS 7, sistemul sincronizează automat UserDefaults în fundal, iar Apple a declarat oficial synchronize() ca fiind redundantă. Apelarea acestei metode nu provoacă o eroare, dar nu oferă nicio garanție suplimentară de salvare.
Pentru monitorizarea modificărilor, NSUserDefaults oferă notificarea UserDefaults.didChangeNotification și metoda de observare KVO addObserver(_:forKeyPath:options:context:). În SwiftUI este disponibil Property Wrapper @AppStorage, care sincronizează automat valoarea din UserDefaults cu actualizarea UI. @AppStorage suportă aceleași tipuri ca UserDefaults și este modalitatea preferată de lucru cu setările în aplicațiile SwiftUI.
// Observarea modificărilor prin KVO
class SettingsViewModel: NSObject {
override func observeValue(
forKeyPath keyPath: String?,
of object: Any?,
change: [NSKeyValueChangeKey: Any]?,
context: UnsafeMutableRawPointer?
) {
guard let keyPath else { return }
print("Cheia s-a schimbat: \(keyPath)")
}
}
// SwiftUI - AppStorage
struct SettingsView: View {
@AppStorage("theme") private var theme: String = "system"
var body: some View {
Picker("Temă", selection: $theme) {
Text("Sistem").tag("system")
Text("Luminoasă").tag("light")
Text("Întunecată").tag("dark")
}
}
}
Pentru lucrul cu App Groups (stocare partajată între aplicație și extensii) se utilizează inițializatorul UserDefaults(suiteName:) cu identificatorul App Group. De exemplu, "group.com.example.myapp". Datele scrise în această instanță sunt accesibile din aplicația principală, widget, aplicația companion watchOS și alte extensii aparținând aceluiași App Group. Fiecare instanță suite este stocată într-un fișier plist separat.
În ciuda confortului și simplității, NSUserDefaults nu este un stoc universal pentru toate tipurile de date pe iOS. În funcție de volum, criticitate și cerințe de securitate, Apple oferă mai multe alternative, fiecare optimizată pentru un scenariu specific de utilizare.
| Soluție | Când să utilizați | Limitări |
|---|---|---|
| NSUserDefaults | Setări interfață și configurare | Nu este potrivit pentru date mari și secrete |
| Keychain | Parole, tokenuri, chei de criptare | Mai complex de utilizat, mai lent |
| CoreData | Date structurate cu relații | Excesiv pentru 10-20 de setări |
| FileManager | Documente, imagini, date binare | Necesită gestionare manuală a fișierelor |
| CloudKit | Sincronizare în cloud între dispozitive | Necesită cont iCloud și conexiune de rețea |
Keychain este un stoc protejat Apple pentru date confidențiale. Spre deosebire de NSUserDefaults, toate datele din Keychain sunt criptate la nivelul sistemului de operare utilizând criptarea hardware Secure Enclave pe dispozitivele compatibile. Keychain se blochează și deblochează automat odată cu dispozitivul și suportă partajarea accesului între aplicațiile aceluiași dezvoltator prin Keychain Access Groups.
Principalul dezavantaj al Keychain este complexitatea API-ului. Pentru a salva simplu un șir de caractere, trebuie creată o cerere SecItemAdd cu specificarea atributelor: clasă (kSecClassGenericPassword), serviciu (kSecAttrService), cont (kSecAttrAccount) și datele propriu-zise (kSecValueData). Pentru simplificarea lucrului cu Keychain există împachetări terțe, precum KeychainAccess și SwiftKeychainWrapper, care oferă o interfață convenabilă cheie-valoare similară cu UserDefaults.
Să analizăm un exemplu practic: salvarea și restaurarea stării de onboarding (ecrane de bun venit) într-o aplicație iOS utilizând NSUserDefaults. La prima lansare, utilizatorul vede ecranele de onboarding, după parcurgerea cărora un flag este salvat în UserDefaults. La lansările ulterioare, onboarding-ul este sărit. Pentru SwiftUI se folosește @AppStorage, pentru UIKit — acces direct la UserDefaults.standard.
Să creăm un manager OnboardingManager care încapsulează lucrul cu UserDefaults pentru stocarea stării de onboarding. Managerul oferă proprietatea isOnboardingCompleted pentru verificarea stării și metoda markOnboardingCompleted pentru setarea flag-ului. Cheia de stocare este extrasă într-o constantă pentru prevenirea greșelilor de tastare. Pentru testare unitară, managerul folosește protocolul UserDefaultsProtocol, care permite înlocuirea depozitului real cu 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)
}
}
// Utilizare în aplicație
let onboardingManager = OnboardingManager()
if !onboardingManager.isOnboardingCompleted {
showOnboarding()
} else {
showMainScreen()
}
Pentru stocarea setărilor mai complexe, precum un obiect structurat Profile, se recomandă utilizarea protocolului Codable și JSONEncoder/JSONDecoder. Obiectul este serializat în Data prin JSONEncoder, salvat prin set(_:forKey:), iar la citire este deserializat din Data înapoi în obiect prin JSONDecoder. Această abordare permite stocarea structurilor complexe în UserDefaults fără pierderea siguranței tipurilor.
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)
}
}
// Utilizare
let profile = UserProfile(name: "Ana", age: 28, preferences: ["theme": "dark"])
UserDefaults.standard.save(profile, forKey: "user_profile")
let loaded = UserDefaults.standard.load(UserProfile.self, forKey: "user_profile")
Este important de reținut că NSUserDefaults nu este destinat stocării volumelor mari de date. Apple recomandă limitarea dimensiunii datelor stocate la câteva zeci de kilobytes. Pentru stocarea obiectelor mari (imagini, documente, modele serializate) trebuie utilizat FileManager cu directorul Documents sau CoreData. În plus, UserDefaults nu suportă versionarea schemei de date — la modificarea structurii modelului Codable, datele vechi pot să nu se deserializeze, iar acest lucru trebuie gestionat în codul aplicației.
Întrebări frecvente
Ambele sunt depozite cheie-valoare, dar NSUserDefaults suportă mai multe tipuri (Data, Date, Array, Dictionary) și se sincronizează automat cu iCloud. SharedPreferences stochează datele în XML, NSUserDefaults — în format plist. NSUserDefaults are un sistem de domenii cu căutare în cascadă, SharedPreferences folosește o structură plată simplă cu nume de fișiere.
Nu, NSUserDefaults stochează datele în formă deschisă fără criptare. Pentru parole, tokenuri și chei de criptare utilizați Keychain, care criptează datele la nivelul Secure Enclave. Keychain suportă, de asemenea, atribute de acces precum autentificarea biometrică (Face ID / Touch ID) înainte de citirea secretului.
Pentru sincronizarea între dispozitivele aceluiași utilizator utilizați NSUbiquitousKeyValueStore — depozitul cloud cheie-valoare iCloud. Datele scrise în acest serviciu pe un dispozitiv apar automat pe toate celelalte dispozitive ale aceluiași cont iCloud. Volum maxim — 1 MB per aplicație, 1024 chei.
Pentru ștergerea tuturor datelor, apelați metoda removePersistentDomain(forName:) cu Bundle Identifier al aplicației. Pentru ștergerea valorilor individuale utilizați removeObject(forKey:). Pentru resetarea completă a setărilor aplicației: UserDefaults.standard.removePersistentDomain(forName: Bundle.main.bundleIdentifier!). Toate ștergerile se aplică imediat cache-ului din memorie.
Apple nu stabilește o limită strictă pentru dimensiunea NSUserDefaults, dar se recomandă să nu depășiți 100 KB pentru volumul total al tuturor datelor stocate. Pentru volume mari utilizați CoreData sau FileManager. La stocarea a peste 1 MB de date, performanța de citire la pornirea aplicației poate scădea considerabil.
Rezumat
Vom dezvolta o aplicație mobilă la cheie
IT Sectr creează aplicații iOS și Android pentru startup-uri și afaceri din 2017. Vă vom consilia și vă vom propune cea mai bună soluție.
Citiți și