NSUserDefaults é um armazenamento de dados chave-valor no iOS, watchOS, tvOS e macOS, projetado para salvar configurações e preferências da aplicação. Os dados são armazenados num ficheiro plist na sandbox da aplicação e sincronizam automaticamente com iCloud através de NSUbiquitousKeyValueStore. De acordo com a documentação oficial da Apple Developer, 2025, o NSUserDefaults suporta armazenamento de tipos primitivos: String, Int, Bool, Float, Double, Data, Date, Array e Dictionary. A classe foi renomeada para UserDefaults a partir do Swift 3, mas o seu nome Objective-C NSUserDefaults continua amplamente utilizado em bases de código e documentação Apple.
Pontos Principais
NSUserDefaults (UserDefaults em Swift) é o mecanismo integrado da Apple para armazenar pares chave-valor em formato plist. Está disponível em todas as plataformas Apple: iOS, iPadOS, watchOS, tvOS e macOS. O seu principal objetivo é salvar preferências do utilizador, estado da interface, indicadores de primeiro lançamento, opções selecionadas e outros dados simples que persistem após reinícios da aplicação.
Cada aplicação iOS tem uma sandbox isolada, e o NSUserDefaults é armazenado no diretório Library/Preferences dentro desta sandbox num ficheiro com o nome do Bundle Identifier. O ficheiro plist contém pares chave-valor onde a chave é uma string e o valor é um dos tipos suportados. O tamanho do ficheiro não é limitado, mas a Apple recomenda armazenar apenas configurações no UserDefaults, não grandes quantidades de dados.
A partir do iOS 8, o NSUserDefaults começou a suportar App Groups — armazenamento partilhado entre aplicações do mesmo desenvolvedor e as suas extensões (widgets, aplicações complementares watchOS). Para isso, usa-se o inicializador init?(suiteName:) com um identificador de App Group. Isto permite, por exemplo, que um widget no ecrã Today leia configurações da aplicação principal sem duplicar a lógica de salvaguarda.
Fisicamente, o NSUserDefaults é armazenado num ficheiro plist binário em: {Sandbox}/Library/Preferences/com.example.myapp.plist. O ficheiro utiliza o formato plist binário (NSPropertyListBinaryFormat_v1_0) para compacidade e velocidade de leitura. No macOS, o ficheiro pode estar em formato XML para compatibilidade. Ao contrário do SharedPreferences no Android, os ficheiros plist do UserDefaults podem conter estruturas aninhadas através de Dictionary e Array.
Os ficheiros NSUserDefaults não são encriptados por padrão. Os dados são armazenados em texto simples e podem ser lidos com acesso físico ao dispositivo ou através de uma cópia de segurança. Para armazenar dados sensíveis (palavras-passe, tokens, chaves de encriptação), a Apple recomenda fortemente usar Keychain, que encripta automaticamente os dados a nível do sistema operativo.
O NSUserDefaults funciona com base no princípio de cache em memória com sincronização periódica em disco. No primeiro acesso à instância padrão UserDefaults.standard, o sistema carrega o ficheiro plist para a RAM como um Dictionary. Todas as leituras subsequentes são realizadas a partir da memória. A escrita também ocorre primeiro em memória, com sincronização em disco a ocorrer periodicamente numa thread em segundo plano.
As operações de escrita usam o método set(_:forKey:), que aceita um valor opcional do tipo Any?. O valor pode ser nil — usado para remover uma chave. Para escrita imediata em disco, o método synchronize() era usado anteriormente, mas a partir do iOS 7 e OS X 10.9 já não é necessário — o sistema sincroniza automaticamente os dados em intervalos regulares. A Apple declarou oficialmente o synchronize() como redundante na sua documentação.
O NSUserDefaults utiliza um sistema de registos (domínios) para organizar a pesquisa de valores. Quando uma aplicação solicita um valor por chave, o UserDefaults verifica sequencialmente os domínios numa ordem específica: primeiro NSArgumentDomain (argumentos de linha de comando), depois o domínio da Aplicação, depois NSGlobalDomain (configurações do sistema), depois domínios específicos de idioma e finalmente NSRegistrationDomain (valores padrão registados via register(defaults:)).
import Foundation
// Instância padrão do UserDefaults
let defaults = UserDefaults.standard
// Escrita de valores
defaults.set("Ana Petrova", forKey: "username")
defaults.set(28, forKey: "age")
defaults.set(true, forKey: "isLoggedIn")
// Registo de valores padrão
defaults.register(defaults: [
"theme": "system",
"fontSize": 14
])
// Leitura com devolução de valor padrão
let theme = defaults.string(forKey: "theme") ?? "system"
let fontSize = defaults.integer(forKey: "fontSize")
O domínio NSRegistrationDomain é um domínio programático que existe apenas em memória e não é persistido em disco. É utilizado para definir valores padrão que se aplicam até que a aplicação escreva o seu próprio valor no domínio da Aplicação. Isto permite criar um ponto único de configuração para definições padrão que podem ser alteradas centralizadamente durante o desenvolvimento.
O NSUserDefaults fornece um conjunto de métodos tipados para ler e escrever dados: string(forKey:), integer(forKey:), bool(forKey:), float(forKey:), double(forKey:), data(forKey:), array(forKey:), dictionary(forKey:) e object(forKey:). Cada método de leitura tem um método de escrita correspondente set(_:forKey:) com determinação automática do tipo do valor armazenado. A versão Swift do UserDefaults usa tipagem forte, mas a versão Objective-C aceita e retorna id.
| Método de leitura (Swift) | Tipo de dados | Valor padrão |
|---|---|---|
| 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 |
O método synchronize() no NSUserDefaults força a escrita de todas as alterações da memória para o disco. Nas versões iniciais do iOS, este método tinha de ser chamado após cada escrita para garantir a persistência dos dados. A partir do iOS 7, o sistema sincroniza automaticamente o UserDefaults numa thread em segundo plano, e a Apple declarou oficialmente o synchronize() como redundante. Chamar este método não causa erro, mas não oferece garantias adicionais de persistência.
Para monitorizar alterações, o NSUserDefaults fornece a notificação UserDefaults.didChangeNotification e o método de observação KVO addObserver(_:forKeyPath:options:context:). No SwiftUI, está disponível o Property Wrapper @AppStorage, que sincroniza automaticamente um valor no UserDefaults com a atualização da UI. O @AppStorage suporta os mesmos tipos que o UserDefaults e é a forma preferida de trabalhar com configurações em aplicações SwiftUI.
// Observação de alterações via KVO
class SettingsViewModel: NSObject {
override func observeValue(
forKeyPath keyPath: String?,
of object: Any?,
change: [NSKeyValueChangeKey: Any]?,
context: UnsafeMutableRawPointer?
) {
guard let keyPath else { return }
print("Chave alterada: \(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("Claro").tag("light")
Text("Escuro").tag("dark")
}
}
}
Para trabalhar com App Groups (armazenamento partilhado entre uma aplicação e as suas extensões), use o inicializador UserDefaults(suiteName:) com um identificador de App Group. Por exemplo, “group.com.example.myapp”. Os dados escritos nesta instância são acessíveis a partir da aplicação principal, widget, aplicação complementar watchOS e outras extensões pertencentes ao mesmo App Group. Cada instância suite é armazenada num ficheiro plist separado.
Apesar da sua conveniência e simplicidade, o NSUserDefaults não é uma solução de armazenamento universal para todos os tipos de dados no iOS. Dependendo do volume, criticidade e requisitos de segurança, a Apple fornece várias alternativas, cada uma otimizada para um caso de uso específico.
| Solução | Quando usar | Limitações |
|---|---|---|
| NSUserDefaults | Configurações de interface e preferências | Não adequado para grandes dados ou segredos |
| Keychain | Palavras-passe, tokens, chaves de encriptação | Mais complexo de usar, mais lento |
| CoreData | Dados estruturados com relações | Excessivo para 10–20 configurações |
| FileManager | Documentos, imagens, dados binários | Requer gestão manual de ficheiros |
| CloudKit | Sincronização na nuvem entre dispositivos | Requer conta iCloud e ligação de rede |
Keychain é o armazenamento seguro da Apple para dados confidenciais. Ao contrário do NSUserDefaults, todos os dados no Keychain são encriptados a nível do sistema operativo usando encriptação de hardware da Secure Enclave em dispositivos compatíveis. O Keychain bloqueia e desbloqueia automaticamente com o dispositivo e suporta partilha de acesso entre aplicações do mesmo desenvolvedor através de Keychain Access Groups.
A principal desvantagem do Keychain é a complexidade da API. Para simplesmente guardar uma string, é necessário criar uma consulta SecItemAdd especificando atributos: classe (kSecClassGenericPassword), serviço (kSecAttrService), conta (kSecAttrAccount) e os dados reais (kSecValueData). Para simplificar o trabalho com Keychain, existem wrappers de terceiros como KeychainAccess e SwiftKeychainWrapper que fornecem uma interface chave-valor conveniente semelhante ao UserDefaults.
Vamos considerar um exemplo prático: salvar e restaurar o estado de onboarding (ecrãs de boas-vindas) numa aplicação iOS usando NSUserDefaults. No primeiro lançamento, o utilizador vê os ecrãs de onboarding; depois de os completar, um indicador é guardado no UserDefaults. Em lançamentos subsequentes, o onboarding é ignorado. Para SwiftUI usa-se @AppStorage; para UIKit, acesso direto a UserDefaults.standard.
Vamos criar um OnboardingManager que encapsula o trabalho com UserDefaults para armazenar o estado do onboarding. O gestor fornece uma propriedade isOnboardingCompleted para verificar o estado e um método markOnboardingCompleted para definir o indicador. A chave de armazenamento é extraída para uma constante para evitar erros tipográficos. Para testes unitários, o gestor usa um protocolo UserDefaultsProtocol, permitindo substituir o armazenamento real por um 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)
}
}
// Uso na aplicação
let onboardingManager = OnboardingManager()
if !onboardingManager.isOnboardingCompleted {
showOnboarding()
} else {
showMainScreen()
}
Para armazenar configurações mais complexas, como um objeto Profile estruturado, recomenda-se usar o protocolo Codable e JSONEncoder/JSONDecoder. O objeto é serializado para Data através de JSONEncoder, guardado via set(_:forKey:), e ao ler é desserializado de Data de volta para o objeto através de JSONDecoder. Esta abordagem permite armazenar estruturas complexas no UserDefaults sem perder a segurança de tipos.
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)
}
}
// Uso
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")
É importante lembrar que o NSUserDefaults não foi projetado para armazenar grandes volumes de dados. A Apple recomenda limitar os dados armazenados a algumas dezenas de kilobytes. Para armazenar objetos grandes (imagens, documentos, modelos serializados), use FileManager com o diretório Documents ou CoreData. Além disso, o UserDefaults não suporta versionamento de esquemas de dados — quando a estrutura do modelo Codable muda, os dados antigos podem não desserializar, e isto deve ser tratado no código da aplicação.
Perguntas Frequentes
Ambos são armazenamentos chave-valor, mas o NSUserDefaults suporta mais tipos (Data, Date, Array, Dictionary) e sincroniza automaticamente com iCloud. O SharedPreferences armazena dados em XML, o NSUserDefaults em formato plist. O NSUserDefaults tem um sistema de domínios com pesquisa em cascata, enquanto o SharedPreferences usa uma estrutura plana simples com nomes de ficheiros.
Não, o NSUserDefaults armazena dados em texto simples sem encriptação. Para palavras-passe, tokens e chaves de encriptação, use Keychain, que encripta os dados ao nível da Secure Enclave. O Keychain também suporta atributos de acesso como autenticação biométrica (Face ID / Touch ID) antes de ler um segredo.
Para sincronização entre dispositivos do mesmo utilizador, use NSUbiquitousKeyValueStore — o armazenamento chave-valor na nuvem iCloud. Os dados escritos neste serviço num dispositivo aparecem automaticamente em todos os outros dispositivos com a mesma conta iCloud. Capacidade máxima: 1 MB por aplicação, 1024 chaves.
Para eliminar todos os dados, chame o método removePersistentDomain(forName:) com o Bundle Identifier da aplicação. Para remover valores individuais, use removeObject(forKey:). Para uma reinicialização completa das definições: UserDefaults.standard.removePersistentDomain(forName: Bundle.main.bundleIdentifier!). Todas as eliminações são aplicadas imediatamente à cache em memória.
A Apple não define um limite rigoroso para o tamanho do NSUserDefaults, mas recomenda-se não exceder 100 KB de volume total de todos os dados armazenados. Para volumes maiores, use CoreData ou FileManager. Ao armazenar mais de 1 MB de dados, o desempenho de leitura no início da aplicação pode diminuir significativamente.
Resumo
Vamos desenvolver um aplicativo móvel chave na mão
A IT Sectr cria aplicativos para iOS e Android para startups e empresas desde 2017. Nós vamos aconselhá-lo e propor a melhor solução.
Leia também