NSUserDefaults — какво е, UserDefaults API и работа с настройките на iOS

Автор: IT Sectr Публикувано: 2026-03-12 Време за четене: 10 мин

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 — хранилище ключ-стойност на iOS и macOS за запазване на прости настройки на приложението в plist файл.
  • Поддържа девет типа данни: String, Int, Bool, Float, Double, Data, Date, Array и Dictionary.
  • Данните автоматично се синхронизират с iCloud чрез NSUbiquitousKeyValueStore с подкрепа от разработчика.
  • Използва система от регистри (домейни) с каскадно търсене на стойност по веригата от домейни.
  • За съхранение на чувствителни данни Apple препоръчва използването на Keychain вместо NSUserDefaults.

Какво е NSUserDefaults?

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 екрана да чете настройки от основното приложение без дублиране на логиката за съхранение.

Формат на съхранение: plist на устройството

Физически, 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 в iOS

NSUserDefaults работи на принципа на кеширане в паметта с периодична синхронизация на диска. При първия достъп до стандартния екземпляр UserDefaults.standard системата зарежда plist файла в RAM паметта под формата на Dictionary. Всички последващи четения се извършват от паметта. Записът също първо се извършва в паметта, а синхронизацията на диска се случва периодично на заден фон.

Операциите за запис използват метода set(_:forKey:), който приема опционална стойност от тип Any?. Стойността може да бъде nil — за премахване на ключ. За незабавен запис на диска преди се използваше методът synchronize(), но от iOS 7 и OS X 10.9 вече не е необходим — системата автоматично синхронизира данни на редовни интервали. Apple официално обяви synchronize() за излишен в своята документация.

Регистри на UserDefaults и домейни

NSUserDefaults използва система от регистри (домейни) за организиране на търсенето на стойности. Когато приложението поиска стойност по ключ, UserDefaults последователно проверява домейните в определен ред: първо NSArgumentDomain (аргументи на командния ред), след това домейна на приложението (Application), след това NSGlobalDomain (системни настройки), след това езиково-специфични домейни и накрая NSRegistrationDomain (стойности по подразбиране, регистрирани чрез register(defaults:)).

swift
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

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:)Int0
bool(forKey:)Boolfalse
float(forKey:)Float0.0
double(forKey:)Double0.0
data(forKey:)Data?nil

synchronize и неговата актуалност

Методът 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 приложения.

swift
// Наблюдение на промени чрез 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 vs алтернативи за съхранение

Въпреки удобството и простотата, NSUserDefaults не е универсално хранилище за всички типове данни на iOS. В зависимост от обема, критичността и изискванията за сигурност, Apple предоставя няколко алтернативи, всяка от които е оптимизирана за конкретен сценарий на използване.

РешениеКога да се използваОграничения
NSUserDefaultsНастройки на интерфейса и конфигурацияНе е подходящо за големи данни и тайни
KeychainПароли, токени, ключове за криптиранеПо-сложно за използване, по-бавно
CoreDataСтруктурирани данни с връзкиПрекалено за 10-20 настройки
FileManagerДокументи, изображения, двоични данниИзисква ръчно управление на файлове
CloudKitОблачна синхронизация между устройстваИзисква iCloud акаунт и мрежова връзка

Keychain — сигурно съхранение

Keychain е защитено хранилище на Apple за поверителни данни. За разлика от NSUserDefaults, всички данни в Keychain са криптирани на ниво операционна система с помощта на хардуерно криптиране Secure Enclave на съвместими устройства. Keychain автоматично се заключва и отключва заедно с устройството и поддържа споделяне на достъп между приложения на един и същ разработчик чрез Keychain Access Groups.

Основният недостатък на Keychain е сложността на API. За просто запазване на низ трябва да се създаде заявка SecItemAdd с посочване на атрибути: клас (kSecClassGenericPassword), услуга (kSecAttrService), акаунт (kSecAttrAccount) и самите данни (kSecValueData). За опростяване на работата с Keychain съществуват външни обвивки, като KeychainAccess и SwiftKeychainWrapper, които предоставят удобен интерфейс ключ-стойност, подобен на UserDefaults.

Пример за използване на NSUserDefaults в Swift

Нека разгледаме практически пример: запазване и възстановяване на състоянието на onboarding (екрани за добре дошли) в iOS приложение с помощта на NSUserDefaults. При първото стартиране потребителят вижда onboarding екраните, след преминаването им флаг се запазва в UserDefaults. При следващи стартирания onboarding се пропуска. За SwiftUI се използва @AppStorage, за UIKit — директен достъп до UserDefaults.standard.

Запазване на състоянието на onboarding

Нека създадем мениджър OnboardingManager, който капсулира работата с UserDefaults за съхранение на статуса на onboarding. Мениджърът предоставя свойството isOnboardingCompleted за проверка на състоянието и метода markOnboardingCompleted за задаване на флага. Ключът за съхранение е изнесен в константа за предотвратяване на правописни грешки. За unit тестване, мениджърът използва протокола UserDefaultsProtocol, който позволява замяна на реалното хранилище с 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)
    }
}

// Използване в приложението
let onboardingManager = OnboardingManager()
if !onboardingManager.isOnboardingCompleted {
    showOnboarding()
} else {
    showMainScreen()
}

За съхранение на по-сложни настройки, като структуриран обект Profile, се препоръчва използването на протокола Codable и JSONEncoder/JSONDecoder. Обектът се сериализира в Data чрез JSONEncoder, запазва се чрез set(_:forKey:), и при четене се десериализира от Data обратно в обект чрез JSONDecoder. Този подход позволява съхраняване на сложни структури в UserDefaults без загуба на типова безопасност.

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

// Използване
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 от SharedPreferences на Android?

И двете са хранилища ключ-стойност, но NSUserDefaults поддържа повече типове (Data, Date, Array, Dictionary) и автоматично се синхронизира с iCloud. SharedPreferences съхранява данни в XML, NSUserDefaults — в plist формат. NSUserDefaults има система от домейни с каскадно търсене, SharedPreferences използва проста плоска структура с имена на файлове.

Безопасно ли е да се съхраняват пароли в NSUserDefaults?

Не, NSUserDefaults съхранява данни в отворен вид без криптиране. За пароли, токени и ключове за криптиране използвайте Keychain, който криптира данни на ниво Secure Enclave. Keychain също поддържа атрибути за достъп като биометрична автентификация (Face ID / Touch ID) преди четене на тайната.

Как да синхронизирам NSUserDefaults между устройства?

За синхронизация между устройства на един и същ потребител използвайте NSUbiquitousKeyValueStore — облачно хранилище ключ-стойност на iCloud. Данните, записани в тази услуга на едно устройство, автоматично се появяват на всички други устройства със същия iCloud акаунт. Максимален обем — 1 MB на приложение, 1024 ключа.

Как да изтрия всички данни от NSUserDefaults?

За изтриване на всички данни извикайте метода removePersistentDomain(forName:) с Bundle Identifier на приложението. За изтриване на отделни стойности използвайте removeObject(forKey:). За пълно нулиране на настройките на приложението: UserDefaults.standard.removePersistentDomain(forName: Bundle.main.bundleIdentifier!). Всички изтривания се прилагат незабавно към кеша в паметта.

Какъв е максималният размер на данните в NSUserDefaults?

Apple не поставя строг лимит за размера на NSUserDefaults, но се препоръчва да не надвишава 100 KB за общия обем на всички съхранявани данни. За големи обеми използвайте CoreData или FileManager. При съхранение на над 1 MB данни, производителността на четене при стартиране на приложението може значително да намалее.

Резюме

  • NSUserDefaults — вградено хранилище ключ-стойност на Apple за прости настройки на приложението в plist формат.
  • Поддържа девет типа данни: String, Int, Bool, Float, Double, Data, Date, Array и Dictionary.
  • Използва система от домейни с каскадно търсене чрез NSRegistrationDomain, NSGlobalDomain и домейна на приложението.
  • За синхронизация между устройства Apple предоставя NSUbiquitousKeyValueStore с лимит от 1 MB на приложение.
  • За сигурност използвайте Keychain за пароли и токени, а не NSUserDefaults.
  • В SwiftUI предпочитаният начин за работа е Property Wrapper @AppStorage с автоматична синхронизация на UI.
  • За големи или структурирани данни изберете CoreData или FileManager вместо NSUserDefaults.

Ще разработим мобилно приложение под ключ

IT Sectr създава iOS и Android приложения за стартъпи и бизнеси от 2017 г. Ще ви консултираме и ще предложим най-доброто решение.

Обсъдете проекта

Прочетете също