@AppStorage في SwiftUI — ما هو، UserDefaults وتخزين الإعدادات

المؤلف: IT Sectr نُشر: 2026-06-25 وقت القراءة: 7 دق

@AppStorage في SwiftUI هو property wrapper للعمل مع UserDefaults يقوم بمزامنة القيمة مع UI تلقائياً. عند تغيير خاصية مُعلنة عبر @AppStorage، يتم حفظ القيمة الجديدة فوراً في UserDefaults، وعند تغيير UserDefaults من الخارج — عبر widget أو extension — يتم إعادة رسم View تلقائياً. وفقاً لـ Apple Developer Documentation (2025)، @AppStorage يدعم String وInt وDouble وBool وData وURL وإصداراتها الاختيارية، مما يوفر تخزيناً تفاعلياً لإعدادات المستخدم دون كتابة كود مراقبة يدوي.

النقاط الرئيسية

  • @AppStorage — property wrapper للعمل التفاعلي مع UserDefaults في SwiftUI
  • الحفظ التلقائي — تُكتب القيمة في UserDefaults عند كل تغيير
  • التحديث التلقائي لواجهة UI — تُعاد رسم View عند تغيير UserDefaults من أي مصدر
  • الأنواع المدعومة: String وInt وDouble وBool وData وURL والإصدارات الاختيارية
  • القيمة الافتراضية تُحدد في التصريح وتُستخدم عند التشغيل الأول

ما هو @AppStorage في SwiftUI؟

@AppStorage هو property wrapper قدمته Apple في iOS 14 يربط خاصية View بمفتاح في UserDefaults. عند قراءة الخاصية، يقوم SwiftUI بتحميل القيمة من UserDefaults باستخدام المفتاح المحدد. عند الكتابة، يحفظ القيمة الجديدة ويُعلم View بضرورة إعادة الرسم.

قبل @AppStorage، كان على المطورين قراءة UserDefaults يدوياً في onAppear، والاشتراك في UserDefaults.didChangeNotification، وتحديث @State عند التغييرات. @AppStorage يؤتمت الدورة بأكملها: تصريح بسطر واحد يحل محل 15–20 سطراً من كود التكرار. علاوةً على ذلك، @AppStorage يوفر مزامنة ثنائية الاتجاه — إذا تغيرت قيمة UserDefaults من عملية أخرى (مثل App Extension أو Widget)، ستستلم View التحديث على أي حال.

من الناحية المعمارية، @AppStorage مطبق كـ DynamicProperty، مما يسمح لـ SwiftUI بتتبع التبعيات وإعادة رسم View عند تغيير القيمة المراقبة. وهذا يجعله مثالياً لتخزين إعدادات المستخدم: لغة الواجهة، تمكين/تعطيل الميزات، آخر تبويب محدد، اسم المستخدم.

@AppStorage مقابل UserDefaults: مقارنة

على الرغم من أن @AppStorage يستخدم UserDefaults داخلياً، فإن طرق العمل مع التخزين تختلف جوهرياً. UserDefaults هو API منخفض المستوى يتطلب إدارة يدوية للقراءة والكتابة وإشعارات التغيير. @AppStorage هو تجريد SwiftUI يوفر سلوكاً تفاعلياً مباشراً من الصندوق.

UserDefaults مناسب للعمليات لمرة واحدة: تحميل الإعدادات عند بدء التشغيل، كتابة التحليلات، تخزين الرموز مؤقتاً. @AppStorage مخصص للإعدادات التي يجب أن تحدث UI بشكل تفاعلي: مفاتيح التبديل للمظهر، اختيار اللغة، حفظ حالة الواجهة. استخدام UserDefaults مباشرة داخل View هو نمط سيئ، لأن View لا تعلم بالتغييرات دون اشتراك إضافي.

المعامل@AppStorageUserDefaults
التفاعليةتلقائيةتتطلب اشتراكاً في الإشعارات
Boilerplateسطر واحد لكل خاصية15–20 سطراً لكل خاصية
الأنواعString، Int، Double، Bool، Data، URLجميع الأنواع + الكائنات المؤرشفة
الأنواع المخصصةعبر RawRepresentableعبر NSKeyedArchiver
App Extensionمزامنة تلقائيةاشتراك يدوي

للإعدادات البسيطة مع UI تفاعلي @AppStorage هو الخيار المفضل. للبيانات المعقدة (المصفوفات، القواميس، الكائنات المخصصة) استخدم مزيجاً من UserDefaults مع @State واشتراك يدوي للتغييرات، أو انتقل إلى SwiftData / Core Data للتخزين المنظم.

أنواع البيانات المدعومة

@AppStorage يدعم الأنواع القياسية التي يمكن لـ UserDefaults تسلسلها مباشرة: String، Int، Double، Bool، Data، URL. لكل نوع يوجد إصدار اختياري (String?، Int?، Double?، Bool?، Data?، URL?)، مما يتيح التمييز بين "غير مضبوط" و"قيمة فارغة".

لتخزين الأنواع المخصصة المتوافقة مع بروتوكول RawRepresentable، يعمل @AppStorage تلقائياً أيضاً. إذا كان لدى enum rawValue من نوع String أو Int، يمكن استخدامه مباشرة: @AppStorage("theme") var theme: AppTheme = .system. يقوم SwiftUI تلقائياً بتسلسل/إلغاء تسلسل القيمة عبر rawValue.

swift
enum AppTheme: String {
    case system, light, dark
}

struct SettingsView: View {
    @AppStorage("username") var username: String = "Guest"
    @AppStorage("launchCount") var launchCount: Int = 0
    @AppStorage("isDarkMode") var isDarkMode: Bool = false
    @AppStorage("appTheme") var theme: AppTheme = .system
    @AppStorage("lastOpened") var lastOpened: Date? = nil

    var body: some View {
        Form {
            TextField("Username", text: $username)
            Toggle("Dark mode", isOn: $isDarkMode)
            Text("تم التشغيل \(launchCount) مرة")
        }
    }
}

يستخدم المثال أنواعاً مختلفة من @AppStorage: String بقيمة افتراضية "Guest"، وInt لعداد مرات التشغيل، وBool للمظهر الداكن، وenum AppTheme مع rawValue من نوع String، وDate? اختياري لآخر وقت فتح. كل خاصية مرتبطة بمفتاح UserDefaults محدد كأول وسيط. تُستخدم القيمة الافتراضية إذا كان المفتاح مفقوداً من التخزين عند التشغيل الأول.

مراقبة تغييرات التخزين

إحدى المزايا الرئيسية لـ @AppStorage هي المراقبة التلقائية لتغييرات UserDefaults من أي مصدر. إذا قام App Extension أو Widget بتغيير قيمة، فإن @AppStorage في التطبيق الرئيسي يستلم الإشعار ويعيد رسم View. يتم تحقيق ذلك من خلال آلية KVO (Key-Value Observing) التي يقوم @AppStorage بإعدادها تلقائياً على UserDefaults.didChangeNotification.

عملياً، هذا يعني أنه إذا قام المستخدم بتغيير إعداد في Widget (مثلاً، تفعيل المظهر الداكن)، فإن التطبيق يلتقط التغيير فوراً. نفس المزامنة تعمل بين التطبيق الرئيسي وShare Extension وWatch App أو Today Widget. لا يحتاج المطور إلى كتابة كود لتبادل البيانات بين العمليات — @AppStorage يفعل ذلك تلقائياً.

swift
struct ThemeSettingView: View {
    @AppStorage("isDarkMode") var isDarkMode: Bool = false

    var body: some View {
        VStack {
            Toggle("Dark Mode", isOn: $isDarkMode)
                .onChange(of: isDarkMode) { oldValue, newValue in
                    print("تغير الوضع الداكن إلى \(newValue)")
                }
        }
    }
}

Toggle مرتبط بـ $isDarkMode عبر @AppStorage. عند التبديل، تُحفظ القيمة تلقائياً في UserDefaults تحت مفتاح "isDarkMode". المُعدل .onChange يسمح بتنفيذ تأثير جانبي عند التغيير — مثلاً، إرسال تحليلات أو تحديث UI شاشات أخرى. إذا قام Widget بتغيير نفس المفتاح، فإن @AppStorage سيشغل أيضاً onChange، مما يضمن اتساق الحالة.

أمثلة كود @AppStorage

لنلقِ نظرة على شاشة إعدادات كاملة للتطبيق تستخدم @AppStorage لتخزين جميع الإعدادات. يحتوي النموذج على أقسام بأنواع مختلفة من الإعدادات: حقول نصية، مفاتيح تبديل، عدادات — جميع القيم تُحفظ تلقائياً في UserDefaults.

swift
struct AppSettingsView: View {
    @AppStorage("displayName") var displayName = ""
    @AppStorage("notificationsEnabled") var notificationsEnabled = true
    @AppStorage("maxResults") var maxResults = 25
    @AppStorage("selectedTab") var selectedTab = "home"

    var body: some View {
        NavigationStack {
            Form {
                Section(header: Text("الملف الشخصي")) {
                    TextField("Display name", text: $displayName)
                }

                Section(header: Text("التفضيلات")) {
                    Toggle("Enable notifications",
                           isOn: $notificationsEnabled)
                    Stepper("Max results: \(maxResults)",
                           value: $maxResults,
                           in: 10...100,
                           step: 5)
                }

                Section {
                    Button("إعادة تعيين الإعدادات") {
                        UserDefaults.standard.removePersistentDomain(
                            forName: Bundle.main.bundleIdentifier!)
                    }
                    .tint(.red)
                }
            }
            .navigationTitle("Settings")
        }
    }
}

يحتوي النموذج على أربع خصائص @AppStorage من أنواع مختلفة: String للاسم، وBool للإشعارات، وInt لعدد النتائج، وString للتبويب المحدد. جميع عناصر التحكم مرتبطة بالخصائص عبر Binding ($displayName، $notificationsEnabled، إلخ). زر "إعادة تعيين الإعدادات" يمسح جميع UserDefaults عن طريق إزالة نطاق التطبيق — بعد ذلك يعود @AppStorage تلقائياً إلى القيم الافتراضية.

مزامنة @AppStorage مع App Group

swift
struct SharedSettingsView: View {
    let sharedDefaults = UserDefaults(suiteName: "group.com.example.app")

    @AppStorage("widgetTheme", store: UserDefaults(suiteName: "group.com.example.app")!)
    var widgetTheme: String = "النظام"

    @AppStorage("widgetColor", store: UserDefaults(suiteName: "group.com.example.app")!)
    var widgetColor: String = "أزرق"

    var body: some View {
        Form {
            Picker("Widget theme", selection: $widgetTheme) {
                Text("النظام").tag("system")
                Text("فاتح").tag("فاتح")
                Text("داكن").tag("داكن")
            }
            Picker("Accent color", selection: $widgetColor) {
                Text("أزرق").tag("blue")
                Text("أخضر").tag("أخضر")
                Text("أحمر").tag("أحمر")
            }
        }
    }
}

من أجل App Group (تخزين مشترك بين التطبيق والإضافات) يقبل @AppStorage المعامل store: UserDefaults(suiteName:). تُحفظ القيم في الحاوية المشتركة المتاحة للتطبيق الرئيسي وWidget وWatch App والإضافات الأخرى من نفس المجموعة. يمكن لـ Widget قراءة هذه الإعدادات، وعند تغييرها في التطبيق، يتم تحديث Widget تلقائياً عبر آلية مراقبة UserDefaults.

الأسئلة الشائعة

ما الفرق بين @AppStorage و @State؟

@State يخزن القيمة في الذاكرة فقط ويُعاد تعيينها عند إعادة تشغيل التطبيق. @AppStorage يحفظ القيمة في UserDefaults ويستعيدها عند التشغيل التالي. استخدم @State للبيانات المؤقتة في الشاشة، و@AppStorage للإعدادات التي يجب أن تبقى بعد إعادة التشغيل.

هل يمكن استخدام @AppStorage مع Enum؟

نعم، إذا كان Enum يطبق بروتوكول RawRepresentable مع rawValue من نوع String أو Int. مثال: @AppStorage("theme") var theme: AppTheme = .system. يقوم SwiftUI تلقائياً بتسلسل enum عبر rawValue ويستعيده عند التحميل.

كيف يمكن مسح جميع قيم @AppStorage؟

استدعِ UserDefaults.standard.removePersistentDomain(forName: Bundle.main.bundleIdentifier!) للتخزين القياسي أو removeObject(forKey:) لمفتاح محدد. بعد المسح، ستعود جميع خصائص @AppStorage إلى القيم الافتراضية المحددة في التصريح.

هل يعمل @AppStorage مع App Extensions؟

نعم، للمزامنة بين التطبيق والإضافات استخدم App Group: @AppStorage("key", store: UserDefaults(suiteName: "group.com.example.app")!). يمكن لـ Widget وShare Extension وWatch App القراءة والكتابة في نفس UserDefaults، ويتم تتبع التغييرات تلقائياً.

ما حجم البيانات التي يمكن تخزينها في @AppStorage؟

@AppStorage يستخدم UserDefaults، وهو مصمم لكميات صغيرة من البيانات: الإعدادات، الرموز، العدادات. الحد الموصى به يصل إلى 100 كيلوبايت لكل تطبيق. للبيانات المنظمة أو الكبيرة (مصفوفات الكائنات، ملفات الوسائط) استخدم SwiftData أو Core Data أو نظام الملفات.

الخلاصة

  • @AppStorage — property wrapper للتخزين التفاعلي للإعدادات في UserDefaults
  • حفظ تلقائي وتحديث تلقائي لواجهة UI عند تغيير القيمة من أي مصدر
  • يدعم String وInt وDouble وBool وData وURL و enums من RawRepresentable
  • القيمة الافتراضية تُحدد في التصريح وتُستعاد عند التشغيل الأول
  • App Group يسمح بمزامنة @AppStorage بين التطبيق والإضافات
  • UserDefaults مناسب فقط لكميات صغيرة من البيانات — حتى 100 كيلوبايت
  • استخدم @AppStorage لإعدادات المستخدم، و@State للحالات المؤقتة للشاشة

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

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

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

اقرأ أيضًا