NSUserDefaults è un archivio dati chiave-valore su iOS, watchOS, tvOS e macOS, progettato per salvare impostazioni e configurazioni dell’applicazione. I dati sono memorizzati in un file plist nella sandbox dell’applicazione e si sincronizzano automaticamente con iCloud tramite NSUbiquitousKeyValueStore. Secondo la documentazione ufficiale di Apple Developer, 2025, NSUserDefaults supporta la memorizzazione di tipi primitivi: String, Int, Bool, Float, Double, Data, Date, Array e Dictionary. La classe è stata rinominata in UserDefaults a partire da Swift 3, ma il suo nome Objective-C NSUserDefaults rimane ampiamente utilizzato nelle basi di codice e nella documentazione Apple.
Punti chiave
NSUserDefaults (UserDefaults in Swift) è il meccanismo integrato di Apple per memorizzare coppie chiave-valore in formato plist. È disponibile su tutte le piattaforme Apple: iOS, iPadOS, watchOS, tvOS e macOS. Il suo scopo principale è salvare le preferenze dell’utente, lo stato dell’interfaccia, i flag di primo avvio, le opzioni selezionate e altri dati semplici che persistono dopo i riavvii dell’applicazione.
Ogni applicazione iOS ha una sandbox isolata e NSUserDefaults è memorizzato nella directory Library/Preferences all’interno di questa sandbox in un file con il nome del Bundle Identifier. Il file plist contiene coppie chiave-valore dove la chiave è una stringa e il valore è uno dei tipi supportati. La dimensione del file non è limitata, ma Apple raccomanda di memorizzare solo impostazioni in UserDefaults, non grandi quantità di dati.
A partire da iOS 8, NSUserDefaults ha iniziato a supportare App Groups — archiviazione condivisa tra applicazioni dello stesso sviluppatore e le loro estensioni (widget, app companion watchOS). Per questo si utilizza l’inizializzatore init?(suiteName:) con un identificatore App Group. Ciò consente, ad esempio, a un widget Today di leggere le impostazioni dall’applicazione principale senza duplicare la logica di salvataggio.
Fisicamente, NSUserDefaults è memorizzato in un file plist binario in: {Sandbox}/Library/Preferences/com.example.myapp.plist. Il file utilizza il formato plist binario (NSPropertyListBinaryFormat_v1_0) per compattezza e velocità di lettura. Su macOS, il file può essere in formato XML per compatibilità. A differenza di SharedPreferences su Android, i file plist di UserDefaults possono contenere strutture annidate tramite Dictionary e Array.
I file NSUserDefaults non sono crittografati per impostazione predefinita. I dati sono memorizzati in testo semplice e possono essere letti con accesso fisico al dispositivo o tramite backup. Per memorizzare dati sensibili (password, token, chiavi di crittografia), Apple raccomanda vivamente di utilizzare Keychain, che crittografa automaticamente i dati a livello di sistema operativo.
NSUserDefaults funziona secondo il principio di caching in memoria con sincronizzazione periodica su disco. Al primo accesso all’istanza standard UserDefaults.standard, il sistema carica il file plist in RAM come Dictionary. Tutte le letture successive vengono eseguite dalla memoria. La scrittura avviene anch’essa prima in memoria, con sincronizzazione su disco che avviene periodicamente su un thread in background.
Le operazioni di scrittura utilizzano il metodo set(_:forKey:), che accetta un valore opzionale di tipo Any?. Il valore può essere nil — usato per rimuovere una chiave. Per la scrittura immediata su disco, in precedenza veniva utilizzato il metodo synchronize(), ma a partire da iOS 7 e OS X 10.9 non è più necessario — il sistema sincronizza automaticamente i dati a intervalli regolari. Apple ha dichiarato ufficialmente synchronize() ridondante nella sua documentazione.
NSUserDefaults utilizza un sistema di registri (domini) per organizzare la ricerca dei valori. Quando un’applicazione richiede un valore per chiave, UserDefaults controlla sequenzialmente i domini in un ordine specifico: prima NSArgumentDomain (argomenti della riga di comando), poi il dominio dell’Applicazione, poi NSGlobalDomain (impostazioni di sistema), poi i domini specifici della lingua e infine NSRegistrationDomain (valori predefiniti registrati tramite register(defaults:)).
import Foundation
// Istanza standard di UserDefaults
let defaults = UserDefaults.standard
// Scrittura di valori
defaults.set("Anna Petrova", forKey: "username")
defaults.set(28, forKey: "age")
defaults.set(true, forKey: "isLoggedIn")
// Registrazione di valori predefiniti
defaults.register(defaults: [
"theme": "system",
"fontSize": 14
])
// Lettura con restituzione del valore predefinito
let theme = defaults.string(forKey: "theme") ?? "system"
let fontSize = defaults.integer(forKey: "fontSize")
Il dominio NSRegistrationDomain è un dominio programmatico che esiste solo in memoria e non viene persistito su disco. Viene utilizzato per impostare valori predefiniti che si applicano fino a quando l’applicazione scrive il proprio valore nel dominio dell’Applicazione. Ciò consente di creare un unico punto di configurazione per le impostazioni predefinite che possono essere modificate centralmente durante lo sviluppo.
NSUserDefaults fornisce un insieme di metodi tipizzati per leggere e scrivere dati: string(forKey:), integer(forKey:), bool(forKey:), float(forKey:), double(forKey:), data(forKey:), array(forKey:), dictionary(forKey:) e object(forKey:). Ogni metodo di lettura ha un corrispondente metodo di scrittura set(_:forKey:) con determinazione automatica del tipo del valore memorizzato. La versione Swift di UserDefaults utilizza tipizzazione forte, ma la versione Objective-C accetta e restituisce id.
| Metodo di lettura (Swift) | Tipo di dati | Valore predefinito |
|---|---|---|
| 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 |
Il metodo synchronize() in NSUserDefaults forza la scrittura di tutte le modifiche dalla memoria al disco. Nelle prime versioni di iOS, questo metodo doveva essere chiamato dopo ogni scrittura per garantire la persistenza dei dati. A partire da iOS 7, il sistema sincronizza automaticamente UserDefaults su un thread in background e Apple ha ufficialmente dichiarato synchronize() ridondante. La chiamata di questo metodo non causa un errore ma non offre garanzie aggiuntive di persistenza.
Per monitorare le modifiche, NSUserDefaults fornisce la notifica UserDefaults.didChangeNotification e il metodo di osservazione KVO addObserver(_:forKeyPath:options:context:). In SwiftUI, è disponibile il Property Wrapper @AppStorage, che sincronizza automaticamente un valore in UserDefaults con l’aggiornamento dell’interfaccia utente. @AppStorage supporta gli stessi tipi di UserDefaults ed è il metodo preferito per lavorare con le impostazioni nelle applicazioni SwiftUI.
// Osservazione delle modifiche tramite KVO
class SettingsViewModel: NSObject {
override func observeValue(
forKeyPath keyPath: String?,
of object: Any?,
change: [NSKeyValueChangeKey: Any]?,
context: UnsafeMutableRawPointer?
) {
guard let keyPath else { return }
print("Chiave modificata: \(keyPath)")
}
}
// SwiftUI - AppStorage
struct SettingsView: View {
@AppStorage("theme") private var theme: String = "system"
var body: some View {
Picker("Tema", selection: $theme) {
Text("Sistema").tag("system")
Text("Chiaro").tag("light")
Text("Scuro").tag("dark")
}
}
}
Per lavorare con App Groups (archiviazione condivisa tra un’applicazione e le sue estensioni), utilizzare l’inizializzatore UserDefaults(suiteName:) con un identificatore App Group. Ad esempio, “group.com.example.myapp”. I dati scritti in questa istanza sono accessibili dall’applicazione principale, dal widget, dall’app companion watchOS e da altre estensioni appartenenti allo stesso App Group. Ogni istanza suite è memorizzata in un file plist separato.
Nonostante la sua comodità e semplicità, NSUserDefaults non è una soluzione di archiviazione universale per tutti i tipi di dati su iOS. A seconda del volume, della criticità e dei requisiti di sicurezza, Apple fornisce diverse alternative, ciascuna ottimizzata per un caso d’uso specifico.
| Soluzione | Quando utilizzare | Limitazioni |
|---|---|---|
| NSUserDefaults | Impostazioni dell’interfaccia e configurazione | Non adatto per grandi dati o segreti |
| Keychain | Password, token, chiavi di crittografia | Più complesso da usare, più lento |
| CoreData | Dati strutturati con relazioni | Eccessivo per 10–20 impostazioni |
| FileManager | Documenti, immagini, dati binari | Richiede gestione manuale dei file |
| CloudKit | Sincronizzazione cloud tra dispositivi | Richiede account iCloud e connessione di rete |
Keychain è l’archiviazione sicura di Apple per dati riservati. A differenza di NSUserDefaults, tutti i dati in Keychain sono crittografati a livello di sistema operativo utilizzando la crittografia hardware di Secure Enclave sui dispositivi compatibili. Keychain si blocca e sblocca automaticamente con il dispositivo e supporta la condivisione dell’accesso tra applicazioni dello stesso sviluppatore tramite Keychain Access Groups.
Lo svantaggio principale di Keychain è la complessità dell’API. Per semplicemente salvare una stringa, è necessario creare una query SecItemAdd specificando gli attributi: classe (kSecClassGenericPassword), servizio (kSecAttrService), account (kSecAttrAccount) e i dati effettivi (kSecValueData). Per semplificare il lavoro con Keychain, esistono wrapper di terze parti come KeychainAccess e SwiftKeychainWrapper che forniscono un’interfaccia chiave-valore comoda simile a UserDefaults.
Consideriamo un esempio pratico: salvare e ripristinare lo stato di onboarding (schermate di benvenuto) in un’applicazione iOS utilizzando NSUserDefaults. Al primo avvio, l’utente vede le schermate di onboarding; dopo averle completate, un flag viene salvato in UserDefaults. Negli avvii successivi, l’onboarding viene saltato. Per SwiftUI si utilizza @AppStorage; per UIKit, accesso diretto a UserDefaults.standard.
Creiamo un OnboardingManager che incapsula il lavoro con UserDefaults per memorizzare lo stato di onboarding. Il gestore fornisce una proprietà isOnboardingCompleted per verificare lo stato e un metodo markOnboardingCompleted per impostare il flag. La chiave di archiviazione è estratta in una costante per prevenire errori di battitura. Per i test unitari, il gestore utilizza un protocollo UserDefaultsProtocol, consentendo di sostituire l’archivio reale con un 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)
}
}
// Utilizzo nell’applicazione
let onboardingManager = OnboardingManager()
if !onboardingManager.isOnboardingCompleted {
showOnboarding()
} else {
showMainScreen()
}
Per memorizzare impostazioni più complesse, come un oggetto Profile strutturato, si consiglia di utilizzare il protocollo Codable e JSONEncoder/JSONDecoder. L’oggetto viene serializzato in Data tramite JSONEncoder, salvato tramite set(_:forKey:), e alla lettura viene deserializzato da Data nell’oggetto tramite JSONDecoder. Questo approccio consente di memorizzare strutture complesse in UserDefaults senza perdere la sicurezza dei tipi.
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)
}
}
// Utilizzo
let profile = UserProfile(name: "Anna", age: 28, preferences: ["theme": "dark"])
UserDefaults.standard.save(profile, forKey: "user_profile")
let loaded = UserDefaults.standard.load(UserProfile.self, forKey: "user_profile")
È importante ricordare che NSUserDefaults non è progettato per memorizzare grandi quantità di dati. Apple raccomanda di limitare i dati memorizzati a poche decine di kilobyte. Per memorizzare oggetti di grandi dimensioni (immagini, documenti, modelli serializzati), utilizzare FileManager con la directory Documents o CoreData. Inoltre, UserDefaults non supporta il versioning dello schema dei dati — quando la struttura del modello Codable cambia, i vecchi dati potrebbero non deserializzarsi e questo deve essere gestito nel codice dell’applicazione.
Domande frequenti
Entrambi sono archivi chiave-valore, ma NSUserDefaults supporta più tipi (Data, Date, Array, Dictionary) e si sincronizza automaticamente con iCloud. SharedPreferences memorizza i dati in XML, NSUserDefaults in formato plist. NSUserDefaults ha un sistema di domini con ricerca a cascata, mentre SharedPreferences utilizza una semplice struttura piatta con nomi di file.
No, NSUserDefaults memorizza i dati in testo semplice senza crittografia. Per password, token e chiavi di crittografia, utilizzare Keychain, che crittografa i dati a livello di Secure Enclave. Keychain supporta anche attributi di accesso come l’autenticazione biometrica (Face ID / Touch ID) prima di leggere un segreto.
Per la sincronizzazione tra dispositivi dello stesso utente, utilizzare NSUbiquitousKeyValueStore — l’archivio chiave-valore cloud iCloud. I dati scritti in questo servizio su un dispositivo appaiono automaticamente su tutti gli altri dispositivi con lo stesso account iCloud. Capacità massima: 1 MB per applicazione, 1024 chiavi.
Per eliminare tutti i dati, chiamare il metodo removePersistentDomain(forName:) con il Bundle Identifier dell’applicazione. Per rimuovere singoli valori, utilizzare removeObject(forKey:). Per un reset completo delle impostazioni: UserDefaults.standard.removePersistentDomain(forName: Bundle.main.bundleIdentifier!). Tutte le eliminazioni vengono applicate immediatamente alla cache in memoria.
Apple non impone un limite rigido alla dimensione di NSUserDefaults, ma si raccomanda di non superare 100 KB di volume totale di tutti i dati memorizzati. Per volumi maggiori, utilizzare CoreData o FileManager. Quando si memorizzano più di 1 MB di dati, le prestazioni di lettura all’avvio dell’applicazione possono diminuire notevolmente.
Riepilogo
Svilupperemo un'applicazione mobile chiavi in mano
IT Sectr crea applicazioni iOS e Android per startup e aziende dal 2017. Ti consulteremo e ti proporremo la soluzione migliore.
Leggi anche