NSUserDefaults — co to jest, UserDefaults API i praca z ustawieniami iOS

Autor: IT Sectr Opublikowano: 2026-03-12 Czas czytania: 10 min

NSUserDefaults to magazyn danych klucz-wartość w iOS, watchOS, tvOS i macOS, przeznaczony do zapisywania ustawień i konfiguracji aplikacji. Dane przechowywane są w pliku plist w piaskownicy aplikacji i automatycznie synchronizowane z iCloud przez NSUbiquitousKeyValueStore. Według oficjalnej dokumentacji Apple Developer, 2025, NSUserDefaults obsługuje przechowywanie typów prymitywnych: String, Int, Bool, Float, Double, Data, Date, Array i Dictionary. Klasa została przemianowana na UserDefaults od Swift 3, ale jej Objective-C nazwa NSUserDefaults pozostaje szeroko używana w bazie kodowej i dokumentacji Apple.

Najważniejsze

  • NSUserDefaults — magazyn klucz-wartość na iOS i macOS do zapisywania prostych ustawień aplikacji w pliku plist.
  • Obsługuje dziewięć typów danych: String, Int, Bool, Float, Double, Data, Date, Array i Dictionary.
  • Dane są automatycznie synchronizowane z iCloud przez NSUbiquitousKeyValueStore przy wsparciu dewelopera.
  • Używa systemu rejestrów (domen) z kaskadowym wyszukiwaniem wartości po łańcuchu domen.
  • Do przechowywania wrażliwych danych Apple zaleca używanie Keychain zamiast NSUserDefaults.

Czym jest NSUserDefaults?

NSUserDefaults (UserDefaults w Swift) to wbudowany mechanizm Apple do przechowywania par klucz-wartość w formacie plist. Jest dostępny na wszystkich platformach Apple: iOS, iPadOS, watchOS, tvOS i macOS. Główne przeznaczenie — zapisywanie preferencji użytkownika, stanu interfejsu, flag first-launch, wybranych opcji i innych prostych danych, które przetrwają restart aplikacji.

Każda aplikacja iOS ma izolowaną piaskownicę, a NSUserDefaults przechowywany jest w katalogu Library/Preferences wewnątrz tej piaskownicy w pliku o nazwie Bundle Identifier. Plik plist zawiera pary klucz-wartość, gdzie kluczem jest ciąg znaków, a wartością jeden z obsługiwanych typów. Rozmiar pliku nie jest ograniczony, ale Apple zaleca przechowywanie w UserDefaults tylko ustawień, a nie dużych ilości danych.

Od iOS 8, NSUserDefaults obsługuje App Groups — współdzielone przechowywanie między aplikacjami tego samego dewelopera i ich rozszerzeniami (widgety, aplikacje towarzyszące watchOS). Służy do tego inicjalizator init?(suiteName:) z identyfikatorem App Group. Pozwala to na przykład widżetowi na ekranie Today odczytywać ustawienia z głównej aplikacji bez duplikowania logiki zapisywania.

Format przechowywania: plist na urządzeniu

Fizycznie NSUserDefaults przechowywany jest w binarnym pliku plist pod ścieżką: {Sandbox}/Library/Preferences/com.example.myapp.plist. Plik używa binarnego formatu plist (NSPropertyListBinaryFormat_v1_0) dla kompaktowości i szybkości odczytu. W macOS plik może być w formacie XML dla zgodności. W przeciwieństwie do SharedPreferences na Androidzie, pliki plist UserDefaults mogą zawierać zagnieżdżone struktury poprzez Dictionary i Array.

Pliki NSUserDefaults nie są domyślnie szyfrowane. Dane przechowywane są w otwartej formie i mogą być odczytane przy fizycznym dostępie do urządzenia lub przez kopię zapasową. Do przechowywania wrażliwych danych (hasła, tokeny, klucze szyfrowania) Apple kategorycznie zaleca używanie Keychain, który automatycznie szyfruje dane na poziomie systemu operacyjnego.

Jak działa NSUserDefaults w iOS

NSUserDefaults działa na zasadzie buforowania w pamięci z okresową synchronizacją na dysk. Przy pierwszym odwołaniu do standardowego egzemplarza UserDefaults.standard system ładuje plik plist do pamięci RAM w formie Dictionary. Wszystkie kolejne odczyty wykonywane są z pamięci. Zapis również najpierw odbywa się w pamięci, a synchronizacja na dysk następuje okresowo w tle.

Operacje zapisu używają metody set(_:forKey:), która przyjmuje wartość opcjonalnego typu Any?. Wartość może być nil — do usunięcia klucza. Do natychmiastowego zapisu na dysk wcześniej używano metody synchronize(), ale od iOS 7 i OS X 10.9 nie jest już wymagana — system automatycznie synchronizuje dane w regularnych odstępach czasu. Apple oficjalnie uznało synchronize() za zbędną w swojej dokumentacji.

Rejestry UserDefaults i domeny

NSUserDefaults używa systemu rejestrów (domen) do organizacji wyszukiwania wartości. Gdy aplikacja żąda wartości po kluczu, UserDefaults kolejno sprawdza domeny w określonej kolejności: najpierw NSArgumentDomain (argumenty wiersza poleceń), następnie domenę aplikacji (Application), potem NSGlobalDomain (ustawienia systemowe), następnie domeny specyficzne dla języka i na końcu NSRegistrationDomain (wartości domyślne zarejestrowane przez register(defaults:)).

swift
import Foundation

// Standardowy egzemplarz UserDefaults
let defaults = UserDefaults.standard

// Zapisywanie wartości
defaults.set("Anna Pietrowa", forKey: "username")
defaults.set(28, forKey: "age")
defaults.set(true, forKey: "isLoggedIn")

// Rejestracja wartości domyślnych
defaults.register(defaults: [
    "theme": "system",
    "fontSize": 14
])

// Odczyt z zwróceniem wartości domyślnej
let theme = defaults.string(forKey: "theme") ?? "system"
let fontSize = defaults.integer(forKey: "fontSize")

Domena NSRegistrationDomain to domena programowa, która istnieje tylko w pamięci RAM i nie jest zapisywana na dysk. Służy do ustawiania wartości domyślnych, które działają, dopóki aplikacja nie zapisze swojej wartości w domenie aplikacji. Pozwala to na utworzenie jednolitego punktu konfiguracji domyślnych ustawień, które można centralnie zmieniać na etapie rozwoju.

Główne metody NSUserDefaults

NSUserDefaults udostępnia zestaw typowanych metod do odczytu i zapisu danych: string(forKey:), integer(forKey:), bool(forKey:), float(forKey:), double(forKey:), data(forKey:), array(forKey:), dictionary(forKey:) i object(forKey:). Każda metoda odczytu ma odpowiadającą jej metodę zapisu set(_:forKey:) z automatycznym określeniem typu przechowywanej wartości. Swiftowa wersja UserDefaults używa ścisłej typizacji, ale wersja Objective-C przyjmuje i zwraca id.

Metoda odczytu (Swift)Typ danychWartość domyślna
string(forKey:)String?nil
integer(forKey:)Int0
bool(forKey:)Boolfalse
float(forKey:)Float0.0
double(forKey:)Double0.0
data(forKey:)Data?nil

synchronize i jego aktualność

Metoda synchronize() w NSUserDefaults wymusza zapis wszystkich zmian z pamięci na dysk. We wczesnych wersjach iOS tę metodę należało wywoływać po każdym zapisie dla gwarancji zachowania danych. Od iOS 7 system automatycznie synchronizuje UserDefaults w tle, a Apple oficjalnie uznało synchronize() za zbędną. Wywołanie tej metody nie powoduje błędu, ale nie daje żadnych gwarancji dodatkowego zachowania danych.

Do monitorowania zmian NSUserDefaults udostępnia powiadomienie UserDefaults.didChangeNotification oraz metodę obserwacji KVO addObserver(_:forKeyPath:options:context:). W SwiftUI dostępny jest Property Wrapper @AppStorage, który automatycznie synchronizuje wartość w UserDefaults z aktualizacją UI. @AppStorage obsługuje te same typy co UserDefaults i jest preferowanym sposobem pracy z ustawieniami w aplikacjach SwiftUI.

swift
// Obserwowanie zmian przez KVO
class SettingsViewModel: NSObject {
    override func observeValue(
        forKeyPath keyPath: String?,
        of object: Any?,
        change: [NSKeyValueChangeKey: Any]?,
        context: UnsafeMutableRawPointer?
    ) {
        guard let keyPath else { return }
        print("Zmienił się klucz: \(keyPath)")
    }
}

// SwiftUI - AppStorage
struct SettingsView: View {
    @AppStorage("theme") private var theme: String = "system"

    var body: some View {
        Picker("Motyw", selection: $theme) {
            Text("Systemowy").tag("system")
            Text("Jasny").tag("light")
            Text("Ciemny").tag("dark")
        }
    }
}

Do pracy z App Groups (współdzielone przechowywanie między aplikacją a rozszerzeniami) używa się inicjalizatora UserDefaults(suiteName:) z identyfikatorem App Group. Na przykład "group.com.example.myapp". Dane zapisane w tym egzemplarzu są dostępne z głównej aplikacji, widżetu, aplikacji towarzyszącej watchOS i innych rozszerzeń należących do jednej App Group. Każdy egzemplarz suite przechowywany jest w osobnym pliku plist.

NSUserDefaults vs alternatywy przechowywania

Pomimo wygody i prostoty, NSUserDefaults nie jest uniwersalnym magazynem dla wszystkich typów danych na iOS. W zależności od objętości, krytyczności i wymagań bezpieczeństwa, Apple udostępnia kilka alternatyw, z których każda jest zoptymalizowana pod konkretny scenariusz użycia.

RozwiązanieKiedy używaćOgraniczenia
NSUserDefaultsUstawienia interfejsu i konfiguracjaNie nadaje się do dużych danych i tajemnic
KeychainHasła, tokeny, klucze szyfrowaniaBardziej skomplikowane w użyciu, wolniejsze
CoreDataDane strukturalne z relacjamiZbędne dla 10-20 ustawień
FileManagerDokumenty, obrazy, dane binarneWymaga ręcznego zarządzania plikami
CloudKitSynchronizacja chmurowa między urządzeniamiWymaga konta iCloud i połączenia sieciowego

Keychain — bezpieczne przechowywanie

Keychain to chronione przechowywanie Apple dla poufnych danych. W przeciwieństwie do NSUserDefaults, wszystkie dane w Keychain są szyfrowane na poziomie systemu operacyjnego z wykorzystaniem sprzętowego szyfrowania Secure Enclave na zgodnych urządzeniach. Keychain automatycznie blokuje się i odblokowuje wraz z urządzeniem oraz obsługuje podział dostępu między aplikacjami tego samego dewelopera przez Keychain Access Groups.

Główną wadą Keychain jest złożoność API. Do prostego zapisania ciągu znaków należy utworzyć żądanie SecItemAdd z określeniem atrybutów: klasy (kSecClassGenericPassword), serwisu (kSecAttrService), konta (kSecAttrAccount) i właściwych danych (kSecValueData). Do uproszczenia pracy z Keychain istnieją zewnętrzne nakładki, takie jak KeychainAccess i SwiftKeychainWrapper, które udostępniają wygodny interfejs klucz-wartość, podobny do UserDefaults.

Przykład użycia NSUserDefaults w Swift

Rozważmy praktyczny przykład: zapisywanie i przywracanie stanu onboardingu (ekranów powitalnych) w aplikacji iOS z użyciem NSUserDefaults. Przy pierwszym uruchomieniu użytkownik widzi ekrany onboardingu, po przejściu których flaga jest zapisywana w UserDefaults. Przy kolejnych uruchomieniach onboarding jest pomijany. Dla SwiftUI używa się @AppStorage, dla UIKit — bezpośredniego dostępu do UserDefaults.standard.

Zapisywanie stanu onboardingu

Stwórzmy menedżer OnboardingManager, który enkapsuluje pracę z UserDefaults do przechowywania statusu onboardingu. Menedżer udostępnia właściwość isOnboardingCompleted do sprawdzania stanu oraz metodę markOnboardingCompleted do ustawienia flagi. Klucz do przechowywania został wyniesiony do stałej, aby zapobiec literówkom. Do testów jednostkowych menedżer używa protokołu UserDefaultsProtocol, co pozwala zastąpić rzeczywiste przechowywanie na MockUserDefaults.

swift
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)
    }
}

// Użycie w aplikacji
let onboardingManager = OnboardingManager()
if !onboardingManager.isOnboardingCompleted {
    showOnboarding()
} else {
    showMainScreen()
}

Do przechowywania bardziej złożonych ustawień, takich jak strukturalny obiekt Profile, zaleca się używanie protokołu Codable oraz JSONEncoder/JSONDecoder. Obiekt jest serializowany do Data przez JSONEncoder, zapisywany przez set(_:forKey:), a przy odczycie deserializowany z Data z powrotem do obiektu przez JSONDecoder. To podejście pozwala przechowywać w UserDefaults złożone struktury bez utraty bezpieczeństwa typów.

swift
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)
    }
}

// Użycie
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")

Ważne jest, aby pamiętać, że NSUserDefaults nie jest przeznaczony do przechowywania dużych ilości danych. Apple zaleca ograniczanie rozmiaru przechowywanych danych do kilkudziesięciu kilobajtów. Do przechowywania dużych obiektów (obrazów, dokumentów, serializowanych modeli) należy używać FileManager z katalogiem Documents lub CoreData. Ponadto UserDefaults nie obsługuje wersjonowania schematu danych — przy zmianie struktury modelu Codable stare dane mogą nie zostać zdeserializowane i należy to obsłużyć w kodzie aplikacji.

Często zadawane pytania

Czym NSUserDefaults różni się od SharedPreferences na Androidzie?

Oba są magazynami klucz-wartość, ale NSUserDefaults obsługuje więcej typów (Data, Date, Array, Dictionary) i automatycznie synchronizuje się z iCloud. SharedPreferences przechowuje dane w XML, NSUserDefaults — w formacie plist. NSUserDefaults ma system domen z kaskadowym wyszukiwaniem, SharedPreferences używa prostej płaskiej struktury z nazwami plików.

Czy bezpieczne jest przechowywanie haseł w NSUserDefaults?

Nie, NSUserDefaults przechowuje dane w otwartej formie bez szyfrowania. Do haseł, tokenów i kluczy szyfrowania używaj Keychain, który szyfruje dane na poziomie Secure Enclave. Keychain obsługuje również atrybuty dostępu, takie jak uwierzytelnianie biometryczne (Face ID / Touch ID) przed odczytem sekretu.

Jak synchronizować NSUserDefaults między urządzeniami?

Do synchronizacji między urządzeniami tego samego użytkownika używaj NSUbiquitousKeyValueStore — chmurowego magazynu klucz-wartość iCloud. Dane zapisane w tym serwisie na jednym urządzeniu automatycznie pojawiają się na wszystkich innych urządzeniach tego samego konta iCloud. Maksymalna objętość — 1 MB na aplikację, 1024 klucze.

Jak usunąć wszystkie dane z NSUserDefaults?

Aby usunąć wszystkie dane, wywołaj metodę removePersistentDomain(forName:) z Bundle Identifier aplikacji. Do usunięcia pojedynczych wartości użyj removeObject(forKey:). Do całkowitego resetu ustawień aplikacji: UserDefaults.standard.removePersistentDomain(forName: Bundle.main.bundleIdentifier!). Wszystkie usunięcia są natychmiast stosowane do pamięci podręcznej in-memory.

Jaki jest maksymalny rozmiar danych w NSUserDefaults?

Apple nie ustanawia ścisłego limitu rozmiaru NSUserDefaults, ale zaleca się nie przekraczać 100 KB całkowitej objętości wszystkich przechowywanych danych. Dla większych objętości używaj CoreData lub FileManager. Przy przechowywaniu ponad 1 MB danych wydajność odczytu przy uruchomieniu aplikacji może zauważalnie spaść.

Podsumowanie

  • NSUserDefaults — wbudowany magazyn klucz-wartość Apple do prostych ustawień aplikacji w formacie plist.
  • Obsługuje dziewięć typów danych: String, Int, Bool, Float, Double, Data, Date, Array i Dictionary.
  • Używa systemu domen z kaskadowym wyszukiwaniem przez NSRegistrationDomain, NSGlobalDomain i domenę aplikacji.
  • Do synchronizacji między urządzeniami Apple udostępnia NSUbiquitousKeyValueStore z limitem 1 MB na aplikację.
  • Dla bezpieczeństwa używaj Keychain do haseł i tokenów, a nie NSUserDefaults.
  • W SwiftUI preferowanym sposobem pracy jest Property Wrapper @AppStorage z automatyczną synchronizacją UI.
  • Dla dużych lub strukturalnych danych wybieraj CoreData lub FileManager zamiast NSUserDefaults.

Opracujemy aplikację mobilną pod klucz

IT Sectr tworzy aplikacje na iOS i Androida dla startupów i firm od 2017 roku. Doradzimy Ci i zaproponujemy najlepsze rozwiązanie.

Omów projekt

Przeczytaj również