@Published هو property wrapper من إطار عمل Combine يقوم تلقائياً بنشر تغييرات خاصية صنف متوافق مع بروتوكول ObservableObject. عندما تتغير قيمة خاصية موسومة بـ @Published، يتلقى SwiftUI إشارة عبر objectWillChange ويعيد رسم جميع المشاهدات المشتركة في ذلك الكائن. وفقاً لوثائق Apple Combine Framework (2025)، يُنشئ @Published Publisher يمكن تحويله إضافياً عبر عوامل Combine: map، filter، debounce وغيرها. هذا يجعل @Published جسراً رئيسياً بين البيانات وواجهة المستخدم في بنية MVVM.
الخلاصة
objectWillChange، مما يؤدي إلى إعادة رسم المشاهدات المشتركة$property — يمكن الاشتراك والدمج وتحويل التدفق@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 يُنشئ عنصرين رئيسيين في وقت الترجمة. الأول هو خاصية مخزنة مع مراقب willSet يستدعي objectWillChange.send() قبل كتابة القيمة الجديدة. الثاني هو الإسقاط $propertyName، الذي يُرجع Published.Publisher يمكن استخدامه مباشرة في خطوط أنابيب Combine.
تأمل الصنف Settings بثلاث خصائص: اثنتان @Published وواحدة عادية:
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 توفر تلقائياً publisher يمكن الوصول إليه عبر الإسقاط $propertyName. هذا يسمح باستخدام عوامل Combine للتصفية والتحويل والدمج والمعالجة المؤجلة للقيم.
سيناريو نموذجي هو البحث مع debounce. حقل الإدخال مرتبط بخاصية @Published searchText، ولكن يجب إرسال الطلب إلى الخادم فقط بعد توقف 300 مللي ثانية. Combine مع $searchText.debounce يحل هذا في سطر واحد:
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 التفاعلي.
مع إصدار 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 في بنية. سيرمي المترجم خطأ: «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:
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():
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 فقط داخل صنف متوافق مع ObservableObject. في البنى، استخدم @State للحالة المحلية أو @Bindable مع macro @Observable في iOS 17+. محاولة استخدام @Published في بنية ستؤدي إلى خطأ في الترجمة.
النهج الصحيح: عيّن القيمة الجديدة بالكامل (array = array + ["new"]). @Published يتتبع استبدال المرجع، وليس تغيير المحتوى. لمجموعات الأنواع المرجعية، استخدم الاستدعاء اليدوي objectWillChange.send() بعد تغيير الحالة الداخلية للعناصر.
@State مصمم للحالة المحلية داخل مشهد واحد ويعمل فقط مع أنواع القيمة. @Published لخصائص ObservableObject التي يمكن قراءتها بواسطة مشاهدات متعددة عبر @ObservedObject أو @EnvironmentObject. @State أبسط، @Published أقوى بفضل التكامل مع Combine.
فقط تلك التي يجب أن تؤدي تغييراتها إلى تحديث واجهة المستخدم. الخصائص للحسابات الداخلية أو التخزين المؤقت أو الأعلام المؤقتة لا تحتاج إلى @Published — هذا يقلل من عمليات إعادة الرسم غير الضرورية. استخدم @Published كإشارة على أن «هذه الخاصية مهمة للواجهة».
SwiftUI يتكامل مع Core Data عبر @FetchRequest و @ObservedObject لـ NSManagedObject. ManagedObject متوافق بالفعل مع ObservableObject، لذلك @Published غير مطلوب — NSManagedObject يبلغ عن التغييرات بنفسه. يُستخدم @Published في طبقة ViewModel بين Core Data و UI لتحويل البيانات.
الملخص
objectWillChange.send()، مولّداً publisher عبر الإسقاط $propertyassign(to: &$property) في Swift 5.9 يسمح باشتراك publisher مباشرة بخاصية @Publishedسنقوم بتطوير تطبيق جوال جاهز
تقدم IT Sectr تطبيقات iOS وAndroid للشركات الناشئة والشركات منذ عام 2017. سوف نقدم لك النصح ونقترح أفضل حل.