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 — спільне сховище між застосунками одного розробника та їхніми розширеннями (віджети, watchOS компаньйон-застосунки). Для цього використовується ініціалізатор init?(suiteName:) з ідентифікатором App Group. Це дозволяє, наприклад, віджету на екрані 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-файл в оперативну пам’ять у вигляді 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 — це програмний домен, який існує лише в оперативній пам’яті та не зберігається на диск. Він використовується для завдання значень за замовчуванням, які діють, поки застосунок не запише своє значення в домен застосунку. Це дозволяє створити єдину точку конфігурації дефолтних налаштувань, які можна централізовано змінювати на етапі розробки.

Основні методи 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 з оновленням інтерфейсу. @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». Дані, записані в цей екземпляр, доступні з основного застосунку, віджета, 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

Розглянемо практичний приклад: збереження та відновлення стану онбордингу (екранів привітання) у iOS-застосунку з використанням NSUserDefaults. При першому запуску користувач бачить екрани онбордингу, після проходження яких прапорець зберігається в UserDefaults. При подальших запусках онбординг пропускається. Для SwiftUI використовується @AppStorage, для UIKit — прямий доступ до UserDefaults.standard.

Збереження стану онбордингу

Створимо менеджер OnboardingManager, який інкапсулює роботу з UserDefaults для зберігання статусу онбордингу. Менеджер надає властивість 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!). Всі видалення застосовуються негайно до in-memory кешу.

Який максимальний розмір даних у 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 з автоматичною синхронізацією інтерфейсу.
  • Для великих або структурованих даних вибирайте CoreData або FileManager замість NSUserDefaults.

Ми розробимо мобільний застосунок під ключ

IT Sectr створює застосунки для iOS та Android для стартапів і бізнесу з 2017 року. Ми проконсультуємо вас і запропонуємо найкраще рішення.

Обговорити проект

Читайте також