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 (UserDefaults у Swift) је уграђени Apple механизам за чување парова кључ-вредност у plist формату. Доступан је на свим Apple платформама: iOS, iPadOS, watchOS, tvOS и macOS. Основна намена — чување корисничких преференци, стања интерфејса, first-launch заставица, изабраних опција и других једноставних података који преживљавају поновно покретање апликације.
Свака iOS апликација има изоловани пешчаник, а NSUserDefaults се чува у директоријуму Library/Preferences унутар овог пешчаника у фајлу са именом Bundle Identifier. plist фајл садржи парове кључ-вредност, где је кључ стринг, а вредност један од подржаних типова. Величина фајла није ограничена, али Apple препоручује чување само подешавања у UserDefaults, а не великих количина података.
Од iOS 8, NSUserDefaults подржава App Groups — заједничко складиште између апликација истог програмера и њихових проширења (виџети, watchOS пратеће апликације). За то се користи иницијализатор init?(suiteName:) са идентификатором App Group. Ово омогућава, на пример, виџету на Today екрану да чита подешавања из главне апликације без дуплирања логике чувања.
Физички, 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 ради на принципу кеширања у меморији са периодичном синхронизацијом на диск. При првом обраћању стандардној инстанци UserDefaults.standard, систем учитава plist фајл у RAM меморију у облику Dictionary. Сва накнадна читања се обављају из меморије. Писање се такође прво врши у меморији, а синхронизација на диск се одвија периодично у позадини.
Операције писања користе метод set(_:forKey:), који прихвата вредност опционог типа Any?. Вредност може бити nil — за уклањање кључа. За тренутно писање на диск раније се користио метод synchronize(), али од iOS 7 и OS X 10.9 више није потребан — систем аутоматски синхронизује податке у редовним интервалима. Apple је званично прогласио synchronize() сувишним у својој документацији.
NSUserDefaults користи систем регистара (домена) за организовање претраге вредности. Када апликација затражи вредност по кључу, UserDefaults узастопно проверава домене одређеним редоследом: прво NSArgumentDomain (аргументи командне линије), затим домен апликације (Application), потом NSGlobalDomain (системска подешавања), затим домене специфичне за језик и на крају NSRegistrationDomain (подразумеване вредности регистроване путем register(defaults:)).
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 пружа скуп типизованих метода за читање и писање података: 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:) | Int | 0 |
| bool(forKey:) | Bool | false |
| float(forKey:) | Float | 0.0 |
| double(forKey:) | Double | 0.0 |
| data(forKey:) | Data? | nil |
Метод 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 апликацијама.
// Праћење промена путем 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 није универзално складиште за све типове података на iOS-у. У зависности од обима, критичности и безбедносних захтева, Apple пружа неколико алтернатива, од којих је свака оптимизована за одређени сценариј коришћења.
| Решење | Када користити | Ограничења |
|---|---|---|
| NSUserDefaults | Подешавања интерфејса и конфигурација | Није погодно за велике податке и тајне |
| Keychain | Лозинке, токени, кључеви за шифровање | Сложеније за коришћење, спорије |
| CoreData | Структурирани подаци са везама | Преобимно за 10-20 подешавања |
| FileManager | Документи, слике, бинарни подаци | Захтева ручно управљање фајловима |
| CloudKit | Облачна синхронизација између уређаја | Захтева iCloud налог и мрежну везу |
Keychain је заштићено Apple складиште за поверљиве податке. За разлику од NSUserDefaults-а, сви подаци у Keychain-у су шифровани на нивоу оперативног система коришћењем хардверског шифровања Secure Enclave на компатибилним уређајима. Keychain се аутоматски закључава и откључава заједно са уређајем и подржава поделу приступа између апликација истог програмера путем Keychain Access Groups.
Главни недостатак Keychain — сложеност API-ја. За једноставно чување стринга потребно је креирати захтев SecItemAdd са навођењем атрибута: класе (kSecClassGenericPassword), сервиса (kSecAttrService), налога (kSecAttrAccount) и самих података (kSecValueData). За поједностављење рада са Keychain-ом постоје спољни омоти, као што су KeychainAccess и SwiftKeychainWrapper, који пружају згодан интерфејс кључ-вредност сличан UserDefaults-у.
Размотримо практичан пример: чување и враћање стања онбординга (екрана добродошлице) у iOS апликацији коришћењем NSUserDefaults. При првом покретању корисник види екране онбординга, након чијег проласка се заставица чува у UserDefaults-у. При наредним покретањима онбординг се прескаче. За SwiftUI се користи @AppStorage, за UIKit — директан приступ UserDefaults.standard.
Направимо менаџер OnboardingManager који инкапсулира рад са UserDefaults-ом за чување статуса онбординга. Менаџер пружа својство isOnboardingCompleted за проверу стања и метод markOnboardingCompleted за постављање заставице. Кључ за чување је издвојен у константу ради спречавања грешака у куцању. За јединично тестирање, менаџер користи протокол UserDefaultsProtocol, што омогућава замену правог складишта на 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)
}
}
// Коришћење у апликацији
let onboardingManager = OnboardingManager()
if !onboardingManager.isOnboardingCompleted {
showOnboarding()
} else {
showMainScreen()
}
За чување сложенијих подешавања, као што је структурирани објекат Profile, препоручује се коришћење Codable протокола и JSONEncoder/JSONDecoder. Објекат се серијализује у Data путем JSONEncoder-а, чува путем set(_:forKey:), а при читању десеријализује из Data назад у објекат путем JSONDecoder-а. Овај приступ омогућава чување сложених структура у UserDefaults-у без губитка типске безбедности.
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 подржава више типова (Data, Date, Array, Dictionary) и аутоматски се синхронизује са iCloud. SharedPreferences чува податке у XML-у, NSUserDefaults — у plist формату. NSUserDefaults има систем домена са каскадним претраживањем, SharedPreferences користи једноставну равну структуру са именима фајлова.
Не, NSUserDefaults чува податке у отвореном облику без шифровања. За лозинке, токене и кључеве за шифровање користите Keychain, који шифрује податке на нивоу Secure Enclave. Keychain такође подржава атрибуте приступа попут биометријске аутентификације (Face ID / Touch ID) пре читања тајне.
За синхронизацију између уређаја истог корисника користите NSUbiquitousKeyValueStore — облачно складиште кључ-вредност iCloud-а. Подаци записани у овај сервис на једном уређају аутоматски се појављују на свим другим уређајима истог iCloud налога. Максимални обим — 1 MB по апликацији, 1024 кључа.
За брисање свих података позовите метод removePersistentDomain(forName:) са Bundle Identifier-ом апликације. За брисање појединачних вредности користите removeObject(forKey:). За потпуни ресет подешавања апликације: UserDefaults.standard.removePersistentDomain(forName: Bundle.main.bundleIdentifier!). Сва брисања се одмах примењују на кеш у меморији.
Apple не поставља строгу границу за величину NSUserDefaults, али се препоручује да не прелази 100 KB укупног обима свих чуваних података. За веће обиме користите CoreData или FileManager. При чувању преко 1 MB података, перформансе читања при покретању апликације могу приметно опасти.
Резиме
Развићемо мобилну апликацију под кључ
IT Sectr креира iOS и Android апликације за стартапе и предузећа од 2017. године. Саветоваћемо вас и предложити најбоље решење.
Прочитајте такође