NSUserDefaults — ما هو، واجهة برمجة UserDefaults والعمل مع إعدادات iOS

المؤلف: IT Sectr نُشر: 2026-03-12 وقت القراءة: 10 دق

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 هو مخزن مفتاح-قيمة على 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 لديه sandbox معزول، ويتم تخزين NSUserDefaults في دليل Library/Preferences داخل هذا sandbox في ملف يحمل اسم Bundle Identifier. يحتوي ملف plist على أزواج مفتاح-قيمة حيث المفتاح هو سلسلة نصية والقيمة هي أحد الأنواع المدعومة. حجم الملف غير محدود، لكن Apple توصي بتخزين الإعدادات فقط في UserDefaults، وليس كميات كبيرة من البيانات.

بدءاً من iOS 8، بدأ NSUserDefaults في دعم App Groups — تخزين مشترك بين تطبيقات لنفس المطور وإضافاتها (widgets وتطبيقات 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.

n

ملفات 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 هو نطاق برمجي موجود فقط في الذاكرة ولا يُحفظ على القرص. يُستخدم لتعيين القيم الافتراضية التي تعمل حتى يقوم التطبيق بكتابة قيمته الخاصة في نطاق التطبيق. يتيح ذلك إنشاء نقطة تهيئة واحدة للإعدادات الافتراضية يمكن تغييرها مركزياً أثناء مرحلة التطوير.

الطرق الرئيسية لـ NSUserDefaults

يوفر NSUserDefaults مجموعة من الطرق المكتوبة (typed methods) لقراءة وكتابة البيانات: string(forKey:) وinteger(forKey:) وbool(forKey:) وfloat(forKey:) وdouble(forKey:) وdata(forKey:) وarray(forKey:) وdictionary(forKey:) وobject(forKey:). كل طريقة قراءة لها طريقة كتابة مقابلة set(_:forKey:) مع تحديد تلقائي لنوع القيمة المخزنة. إصدار Swift من UserDefaults يستخدم كتابة قوية (strong typing)، لكن إصدار 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 مقابل بدائل التخزين

على الرغم من سهولته وبساطته، NSUserDefaults ليس حلاً شاملاً للتخزين لجميع أنواع البيانات في iOS. اعتماداً على الحجم والأهمية ومتطلبات الأمان، توفر Apple عدة بدائل، كل منها مُحسَّن لحالة استخدام محددة.

الحلمتى يُستخدمالقيود
NSUserDefaultsإعدادات الواجهة والتهيئةغير مناسب للبيانات الكبيرة أو الأسرار
Keychainكلمات المرور والرموز ومفاتيح التشفيرأكثر تعقيداً في الاستخدام، أبطأ
CoreDataبيانات منظمة مع علاقاتمفرط لـ 10–20 إعداداً
FileManagerالمستندات والصور والبيانات الثنائيةيتطلب إدارة يدوية للملفات
CloudKitمزامنة سحابية بين الأجهزةيتطلب حساب iCloud واتصال شبكة

Keychain — تخزين آمن

Keychain هو التخزين الآمن من Apple للبيانات السرية. على عكس NSUserDefaults، جميع البيانات في Keychain مشفرة على مستوى نظام التشغيل باستخدام تشفير الأجهزة من Secure Enclave على الأجهزة المتوافقة. يتم قفل Keychain وفتحه تلقائياً مع الجهاز ويدعم مشاركة الوصول بين تطبيقات نفس المطور عبر Keychain Access Groups.

العيب الرئيسي لـ Keychain هو تعقيد واجهة برمجته. لحفظ سلسلة نصية ببساطة، تحتاج إلى إنشاء استعلام SecItemAdd مع تحديد السمات: الفئة (kSecClassGenericPassword) والخدمة (kSecAttrService) والحساب (kSecAttrAccount) والبيانات الفعلية (kSecValueData). لتبسيط العمل مع Keychain، توجد أغلفة من طرف ثالث مثل KeychainAccess وSwiftKeychainWrapper التي توفر واجهة مفتاح-قيمة مريحة مشابهة لـ UserDefaults.

مثال استخدام NSUserDefaults في Swift

لننظر في مثال عملي: حفظ واستعادة حالة الإعداد (شاشات الترحيب) في تطبيق iOS باستخدام NSUserDefaults. عند التشغيل الأول، يرى المستخدم شاشات الإعداد؛ بعد إكمالها، يتم حفظ علامة في UserDefaults. في مرات التشغيل اللاحقة، يتم تخطي الإعداد. لـ SwiftUI يُستخدم @AppStorage؛ لـ UIKit يتم الوصول المباشر إلى UserDefaults.standard.

حفظ حالة الإعداد

لننشئ OnboardingManager يغلف العمل مع UserDefaults لتخزين حالة الإعداد. يوفر المدير خاصية isOnboardingCompleted للتحقق من الحالة وطريقة markOnboardingCompleted لتعيين العلامة. تم استخراج مفتاح التخزين في ثابت لمنع الأخطاء الإملائية. لاختبار الوحدة، يستخدم المدير بروتوكول 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 ميغابايت لكل تطبيق، 1024 مفتاحاً.

كيف حذف جميع البيانات من NSUserDefaults؟

لحذف جميع البيانات، استدعِ طريقة removePersistentDomain(forName:) مع Bundle Identifier للتطبيق. لحذف قيم فردية، استخدم removeObject(forKey:). لإعادة تعيين كامل للإعدادات: UserDefaults.standard.removePersistentDomain(forName: Bundle.main.bundleIdentifier!). جميع عمليات الحذف تُطبق فوراً على ذاكرة التخزين المؤقت.

ما هو الحد الأقصى لحجم البيانات في NSUserDefaults؟

Apple لا تضع حداً صارماً لحجم NSUserDefaults، لكن يُوصى بعدم تجاوز 100 كيلوبايت للحجم الإجمالي لجميع البيانات المخزنة. للأحجام الأكبر، استخدم CoreData أو FileManager. عند تخزين أكثر من 1 ميغابايت من البيانات، قد ينخفض أداء القراءة عند تشغيل التطبيق بشكل ملحوظ.

الملخص

  • NSUserDefaults هو مخزن مفتاح-قيمة مدمج من Apple للإعدادات البسيطة للتطبيقات بتنسيق plist.
  • يدعم تسعة أنواع من البيانات: String وInt وBool وFloat وDouble وData وDate وArray وDictionary.
  • يستخدم نظام نطاقات مع بحث متسلسل عبر NSRegistrationDomain وNSGlobalDomain ونطاق التطبيق.
  • للمزامنة بين أجهزة Apple، توفر Apple NSUbiquitousKeyValueStore بحد 1 ميغابايت لكل تطبيق.
  • للأمان، استخدم Keychain لكلمات المرور والرموز، وليس NSUserDefaults.
  • في SwiftUI، الطريقة المفضلة هي Property Wrapper @AppStorage مع مزامنة تلقائية لواجهة المستخدم.
  • للبيانات الكبيرة أو المنظمة، اختر CoreData أو FileManager بدلاً من NSUserDefaults.

سنقوم بتطوير تطبيق جوال جاهز

تقدم IT Sectr تطبيقات iOS وAndroid للشركات الناشئة والشركات منذ عام 2017. سوف نقدم لك النصح ونقترح أفضل حل.

مناقشة المشروع

اقرأ أيضًا