@Published: ما هو، مبدأ العمل والتطبيق

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

@Published هو property wrapper من إطار عمل Combine يقوم تلقائياً بنشر تغييرات خاصية صنف متوافق مع بروتوكول ObservableObject. عندما تتغير قيمة خاصية موسومة بـ @Published، يتلقى SwiftUI إشارة عبر objectWillChange ويعيد رسم جميع المشاهدات المشتركة في ذلك الكائن. وفقاً لوثائق Apple Combine Framework (2025)، يُنشئ @Published Publisher يمكن تحويله إضافياً عبر عوامل Combine: map، filter، debounce وغيرها. هذا يجعل @Published جسراً رئيسياً بين البيانات وواجهة المستخدم في بنية MVVM.

الخلاصة

  • @Published — property wrapper للنشر التلقائي لتغييرات خصائص ObservableObject في SwiftUI و Combine
  • الآلية: عند تغيير القيمة يتم استدعاء objectWillChange، مما يؤدي إلى إعادة رسم المشاهدات المشتركة
  • Publisher متاح عبر الإسقاط $property — يمكن الاشتراك والدمج وتحويل التدفق
  • ObservedObject و StateObject يشتركان تلقائياً في خصائص @Published — لا حاجة للاشتراك اليدوي
  • iOS 17+ macro @Observable يقدم بديلاً، لكن @Published يبقى المعيار لخطوط أنابيب Combine

ما هو @Published؟

@Published هو property wrapper معرف في وحدة Combine يضيف القدرة على إشعار المشتركين تلقائياً بتغييرات خاصية الصنف. يمكن استخدامه فقط داخل صنف (ليس في بنية) وفقط على خصائص صنف متوافق مع بروتوكول ObservableObject.

عندما تتغير قيمة خاصية @Published، يُنشئ Combine حدثاً عبر publisher مدمج يمكن الوصول إليه عبر بادئة الدولار: $propertyName. هذا publisher هو ObservableObjectPublisher، الذي ينتمي إلى ObservableObject نفسه. يشترك SwiftUI تلقائياً فيه عندما تستخدم إحدى المشاهدات @ObservedObject أو @StateObject، ويعيد رسم المشهد كلما تغيرت أي خاصية @Published داخل الكائن.

وفقاً لكتاب مات نويبورغ «IOS 18 Programming Fundamentals with Swift» (2025)، فإن @Published هو غلاف مناسب لنمط willSet، يستدعي تلقائياً objectWillChange.send(). في الواقع، يقوم المترجم بتوسيع @Published إلى خاصية محسوبة مع مراقب willSet، مما يوفر حملاً زائداً صفرياً في وقت التشغيل مقارنة بالتنفيذ اليدوي.

استخدم @Published لجميع خصائص ObservableObject التي يجب أن تنعكس تغييراتها في الواجهة. بالنسبة للخصائص التي لا تؤثر على واجهة المستخدم، فإن الخصائص المخزنة العادية بدون @Publications تقلل من عمليات إعادة الرسم غير الضرورية.

كيف يعمل @Published

@Published يُنشئ عنصرين رئيسيين في وقت الترجمة. الأول هو خاصية مخزنة مع مراقب willSet يستدعي objectWillChange.send() قبل كتابة القيمة الجديدة. الثاني هو الإسقاط $propertyName، الذي يُرجع Published.Publisher يمكن استخدامه مباشرة في خطوط أنابيب Combine.

تأمل الصنف Settings بثلاث خصائص: اثنتان @Published وواحدة عادية:

swift
class Settings: ObservableObject {
    @Published var username: String = "Guest"
    @Published var isDarkMode = false
    var lastLogin: Date = Date()  // without @Published
}

عندما يتغير username أو isDarkMode، سيعيد SwiftUI رسم جميع المشاهدات المشتركة في نسخة Settings. تغيير lastLogin لن يؤدي إلى إعادة رسم. إذا كنت بحاجة إلى إشعار المشتركين يدوياً بتغيير خاصية عادية، يمكنك استدعاء objectWillChange.send() في مراقب willSet.

تفصيل مهم: @Published ينشر التغييرات فقط عند التعيين المباشر للخاصية. إذا كانت الخاصية من نوع مرجعي (صنف) وتغيرت حالتها الداخلية دون استبدال المرجع، لن يكتشف @Published ذلك. في هذه الحالات، يلزم الإرسال اليدوي للحدث أو التحويل إلى نوع قيمة (بنية).

@Published و Combine

@Published مدمج بشكل وثيق مع Combine — كل خاصية @Published توفر تلقائياً publisher يمكن الوصول إليه عبر الإسقاط $propertyName. هذا يسمح باستخدام عوامل Combine للتصفية والتحويل والدمج والمعالجة المؤجلة للقيم.

سيناريو نموذجي هو البحث مع debounce. حقل الإدخال مرتبط بخاصية @Published searchText، ولكن يجب إرسال الطلب إلى الخادم فقط بعد توقف 300 مللي ثانية. Combine مع $searchText.debounce يحل هذا في سطر واحد:

swift
class SearchViewModel: ObservableObject {
    @Published var searchText = ""
    @Published var results: [String] = []
    private var cancellables = Set<AnyCancellable>()

    init() {
        setupSearchSubscription()
    }

    private func setupSearchSubscription() {
        $searchText
            .debounce(for: .milliseconds(300), scheduler: RunLoop.main)
            .removeDuplicates()
            .sink { [weak self] text in
                self?.performSearch(text)
            }
            .store(in: &cancellables)
    }

    private func performSearch(_ text: String) { }
}

وفقاً لمقال جون ساندلز (Swift by Sundell, 2024)، فإن الجمع بين @Published و Combine هو نمط قياسي لخطوط الأنابيب التفاعلية في تطبيقات SwiftUI: التحقق، debounce، throttle، combineLatest، الدمج مع publishers أخرى. يعمل @Published كجسر بين كود UI الأمري و Combine التفاعلي.

@Published مقابل macro @Observable

مع إصدار iOS 17، قدمت Apple macro @Observable، الذي يقدم نهجاً بديلاً للتفاعلية بدون ObservableObject و @Published. @Observable يتتبع تلقائياً الوصول إلى الخصائص على مستوى القراءة بدلاً من الكتابة، مما يوفر إعادة رسم أكثر دقة — يتم تحديث فقط المشهد الذي يقرأ خاصية معينة تم تغييرها.

ومع ذلك، هذا لا يعني أن @Published قديم. يبقى @Published ضرورياً عندما تكون هناك حاجة للتكامل مع خطوط أنابيب Combine — الإسقاط $propertyName يوفر publisher لا يمتلكه @Observable. بالإضافة إلى ذلك، للتوافق مع iOS 16 والإصدارات الأقدم، @Published+ObservableObject هو الخيار الوحيد. وفقاً لجلسة Apple WWDC 2023 «Discover Observation in SwiftUI»، توصي Apple بـ @Observable للمشاريع الجديدة، لكنها تحتفظ صراحةً بدعم @Published للكود الموجود وسيناريوهات Combine.

عملياً، تستخدم العديد من المشاريع نهجاً هجيناً: نماذج البيانات الجديدة تستخدم @Observable، بينما تبقى ObservableObject الموجودة مع @Published دون إعادة هيكلة. @Published لا غنى عنه أيضاً عندما يكون التحكم الدقيق في النشر مطلوباً — على سبيل المثال، تأخير الإشعار حتى اكتمال تحديث مجمّع لعدة خصائص.

الأخطاء الشائعة مع @Published

الخطأ الأول هو استخدام @Published في بنية. سيرمي المترجم خطأ: «Property wrapper cannot be applied to a computed property» أو «‘@Published’ is only available on members of a class.» يتطلب @Published دلالات المرجع لأن ObservableObjectPublisher هو صنف يجب أن يكون فريداً لكل نسخة.

الخطأ الثاني هو تغيير محتوى خاصية مرجعية دون استبدال المرجع. إذا كانت خاصية @Published من نوع مصفوفة [String] واستدعيت array.append("new")، لن يكتشف @Published التغيير لأن المرجع إلى المصفوفة لم يتغير. الحل: تعيين قيمة جديدة للخاصية array = array + ["new"] أو استخدام objectWillChange.send() يدوياً.

الخطأ الثالث هو عدد مفرط من خصائص @Published. كل خاصية @Published تؤدي إلى إعادة رسم جميع المشاهدات المشتركة في ObservableObject، وليس فقط تلك التي تقرأ تلك الخاصية. وفقاً لـ Point-Free (2025)، فإن تقسيم ObservableObject كبير واحد إلى عدة ObservableObjects صغيرة مع @StateObject و @EnvironmentObject يقلل من عمليات إعادة الرسم غير الضرورية ويحسن الأداء.

أمثلة برمجية

المثال الأول هو ViewModel لنموذج تسجيل مع التحقق. الخاصيتان @Published email و password تؤديان إلى عرض أخطاء التحقق عبر خط أنابيب Combine:

swift
class RegistrationViewModel: ObservableObject {
    @Published var email = ""
    @Published var password = ""
    @Published var emailError: String?
    @Published var isFormValid = false
    private var cancellables = Set<AnyCancellable>()

    init() {
        $email
            .map { $0.contains("@") ? nil : "Invalid email" }
            .assign(to: &$emailError)
            .store(in: &cancellables)

        $email.combineLatest($password)
            .map { !$0.isEmpty && !$1.isEmpty }
            .assign(to: &$isFormValid)
            .store(in: &cancellables)
    }
}

المثال الثاني هو النشر اليدوي لمجموعة من العناصر المرجعية. بدلاً من استبدال المصفوفة بأكملها عند كل تغيير داخل عنصر، يتم استخدام objectWillChange.send():

swift
class TodoItem {
    var title: String
    var isDone = false
    init(title: String) { self.title = title }
}

class TodoListViewModel: ObservableObject {
    @Published var items: [TodoItem] = []

    func toggle(item: TodoItem) {
        item.isDone.toggle()
        self.objectWillChange.send()  // manual notification
    }
}

المثال الثالث هو Assign لخاصية @Published عبر Combine. باستخدام بناء Swift 5.9 الجديد، يمكنك التعيين مباشرة عبر الإسقاط assign(to: &$property) بدون غلاف Optional. هذه هي أقصر طريقة لربط publisher بخاصية @Published دون إنشاء اشتراك.

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

هل يمكن استخدام @Published في بنية؟

لا، يمكن استخدام @Published فقط داخل صنف متوافق مع ObservableObject. في البنى، استخدم @State للحالة المحلية أو @Bindable مع macro @Observable في iOS 17+. محاولة استخدام @Published في بنية ستؤدي إلى خطأ في الترجمة.

كيف يعمل @Published مع المصفوفات والقواميس؟

النهج الصحيح: عيّن القيمة الجديدة بالكامل (array = array + ["new"]). @Published يتتبع استبدال المرجع، وليس تغيير المحتوى. لمجموعات الأنواع المرجعية، استخدم الاستدعاء اليدوي objectWillChange.send() بعد تغيير الحالة الداخلية للعناصر.

كيف يختلف @Published عن @State؟

@State مصمم للحالة المحلية داخل مشهد واحد ويعمل فقط مع أنواع القيمة. @Published لخصائص ObservableObject التي يمكن قراءتها بواسطة مشاهدات متعددة عبر @ObservedObject أو @EnvironmentObject. @State أبسط، @Published أقوى بفضل التكامل مع Combine.

هل كل خاصية ObservableObject تحتاج إلى @Published؟

فقط تلك التي يجب أن تؤدي تغييراتها إلى تحديث واجهة المستخدم. الخصائص للحسابات الداخلية أو التخزين المؤقت أو الأعلام المؤقتة لا تحتاج إلى @Published — هذا يقلل من عمليات إعادة الرسم غير الضرورية. استخدم @Published كإشارة على أن «هذه الخاصية مهمة للواجهة».

كيف يعمل @Published مع Core Data؟

SwiftUI يتكامل مع Core Data عبر @FetchRequest و @ObservedObject لـ NSManagedObject. ManagedObject متوافق بالفعل مع ObservableObject، لذلك @Published غير مطلوب — NSManagedObject يبلغ عن التغييرات بنفسه. يُستخدم @Published في طبقة ViewModel بين Core Data و UI لتحويل البيانات.

الملخص

  • @Published — property wrapper من Combine ينشر تلقائياً تغييرات خصائص ObservableObject لـ SwiftUI وخطوط أنابيب Combine
  • الآلية: مراقب willSet يستدعي objectWillChange.send()، مولّداً publisher عبر الإسقاط $property
  • Combine: @Published يوفر publisher لـ debounce، map، combineLatest وعوامل أخرى — جسر بين UI وخطوط الأنابيب التفاعلية
  • @Observable (iOS 17+) — بديل للمشاريع الجديدة، لكن @Published يبقى المعيار لـ Combine والتوافق العكسي
  • الأخطاء: @Published لا يعمل في البنى، لا يتتبع تغيير الأنواع المرجعية، الإفراط في خصائص @Published يزيد من إعادة الرسم
  • أفضل ممارسة: ضع علامة @Published فقط على الخصائص التي تؤثر على UI، قسم ObservableObjects الكبيرة إلى عدة صغيرة
  • Assign: assign(to: &$property) في Swift 5.9 يسمح باشتراك publisher مباشرة بخاصية @Published

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

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

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

اقرأ أيضًا