Το NSUserDefaults είναι ένας χώρος αποθήκευσης δεδομένων κλειδιού-τιμής σε iOS, watchOS, tvOS και macOS, που προορίζεται για την αποθήκευση ρυθμίσεων και παραμέτρων της εφαρμογής. Τα δεδομένα αποθηκεύονται σε ένα αρχείο plist στο sandbox της εφαρμογής και συγχρονίζονται αυτόματα με το 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. Κύριος σκοπός — αποθήκευση προτιμήσεων χρήστη, κατάστασης διεπαφής, σημαιών πρώτης εκκίνησης, επιλεγμένων επιλογών και άλλων απλών δεδομένων που επιβιώνουν από την επανεκκίνηση της εφαρμογής.
Κάθε εφαρμογή iOS έχει ένα απομονωμένο sandbox, και το NSUserDefaults αποθηκεύεται στον κατάλογο Library/Preferences μέσα σε αυτό το sandbox σε ένα αρχείο με το όνομα Bundle Identifier. Το αρχείο plist περιέχει ζεύγη κλειδιού-τιμής, όπου το κλειδί είναι μια συμβολοσειρά και η τιμή είναι ένας από τους υποστηριζόμενους τύπους. Το μέγεθος του αρχείου δεν είναι περιορισμένο, αλλά η Apple συνιστά την αποθήκευση μόνο ρυθμίσεων στο UserDefaults, όχι μεγάλων όγκων δεδομένων.
Από το iOS 8, το NSUserDefaults υποστηρίζει τα App Groups — κοινόχρηστο χώρο αποθήκευσης μεταξύ εφαρμογών του ίδιου προγραμματιστή και των επεκτάσεών τους (widget, watchOS συνοδευτικές εφαρμογές). Για αυτό χρησιμοποιείται ο αρχικοποιητής init?(suiteName:) με το αναγνωριστικό App Group. Αυτό επιτρέπει, για παράδειγμα, σε ένα widget στην οθόνη 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". Τα δεδομένα που γράφονται σε αυτό το στιγμιότυπο είναι προσβάσιμα από την κύρια εφαρμογή, το widget, τη συνοδευτική εφαρμογή watchOS και άλλες επεκτάσεις που ανήκουν στο ίδιο App Group. Κάθε στιγμιότυπο suite αποθηκεύεται σε ξεχωριστό αρχείο plist.
Παρά την ευκολία και την απλότητα, το NSUserDefaults δεν είναι ένας καθολικός χώρος αποθήκευσης για όλους τους τύπους δεδομένων στο iOS. Ανάλογα με τον όγκο, την κρισιμότητα και τις απαιτήσεις ασφαλείας, η Apple παρέχει πολλές εναλλακτικές, καθεμία βελτιστοποιημένη για ένα συγκεκριμένο σενάριο χρήσης.
| Λύση | Πότε να χρησιμοποιείται | Περιορισμοί |
|---|---|---|
| NSUserDefaults | Ρυθμίσεις διεπαφής και παραμετροποίηση | Δεν είναι κατάλληλο για μεγάλα δεδομένα και μυστικά |
| Keychain | Κωδικοί πρόσβασης, διακριτικά, κλειδιά κρυπτογράφησης | Πιο περίπλοκο στη χρήση, πιο αργό |
| CoreData | Δομημένα δεδομένα με σχέσεις | Υπερβολικό για 10-20 ρυθμίσεις |
| FileManager | Έγγραφα, εικόνες, δυαδικά δεδομένα | Απαιτεί χειροκίνητη διαχείριση αρχείων |
| CloudKit | Συγχρονισμός cloud μεταξύ συσκευών | Απαιτεί λογαριασμό 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 συνιστά τον περιορισμό του μεγέθους των αποθηκευμένων δεδομένων σε μερικές δεκάδες kilobytes. Για την αποθήκευση μεγάλων αντικειμένων (εικόνες, έγγραφα, σειριοποιημένα μοντέλα) πρέπει να χρησιμοποιείται το 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 — τον χώρο αποθήκευσης κλειδιού-τιμής cloud 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. Θα σας συμβουλεύσουμε και θα προτείνουμε την καλύτερη λύση.
Διαβάστε επίσης