@ObservedObject هو Property Wrapper في SwiftUI لمراقبة مثيل ObservableObject يتم تمريره من الخارج. على عكس @StateObject، @ObservedObject لا ينشئ كائنًا — بل يشترك في التغييرات لكائن موجود بالفعل. وفقًا لـ توثيق Apple للمطورين (2025)، يُستخدم @ObservedObject في طرق العرض الفرعية التي تحتاج إلى تتبع البيانات التابعة للوالد. @ObservedObject يوفر اتصالًا تفاعليًا دون إدارة دورة حياة الكائن.
الخلاصة
@ObservedObject هو Property Wrapper يشترك طريقة العرض في تغييرات ObservableObject. ObservableObject هو بروتوكول من إطار Combine يتطلب تنفيذ publisher objectWillChange. عندما تتغير أي خاصية موسومة بـ @Published داخل ObservedObject، يرسل publisher إشارة، ويقوم SwiftUI بإعادة رسم جميع طرق العرض المشتركة عبر @ObservedObject.
السمة الرئيسية لـ @ObservedObject هي غياب الملكية. طريقة العرض ليست مسؤولة عن إنشاء الكائن أو تدميره. يتم إنشاء الكائن في طريقة العرض الأصلية (عبر @StateObject) أو حقنه عبر @EnvironmentObject. طريقة العرض الفرعية تراقب التغييرات فقط وتتلقى التحديثات. إذا تم استبدال الكائن في الأصل، يتحول @ObservedObject إلى المثيل الجديد.
@ObservedObject مناسب لـ سيناريوهات مشاركة البيانات: نموذج المستخدم، الإعدادات المشتركة، حالة الاتصال بالخادم. عندما تحتاج طرق عرض متعددة على مستويات هرمية مختلفة إلى عرض نفس البيانات، ينشئ @ObservedObject في كل طريقة عرض اشتراكات مستقلة ولكن متسقة لمصدر واحد.
الفرق بين @ObservedObject و @StateObject هو أحد أكثر الأسئلة شيوعًا في مقابلات SwiftUI. القاعدة الأساسية: @StateObject ينشئ الكائن ويمتلكه، @ObservedObject يراقب كائنًا موجودًا بالفعل. انتهاك هذه القاعدة يؤدي إلى فقدان غير متوقع للبيانات أو تهيئة مزدوجة.
| الخاصية | @StateObject | @ObservedObject |
|---|---|---|
| إنشاء الكائن | نعم، عند تهيئة العرض | لا، يستقبل كائنًا جاهزًا |
| الملكية | العرض الحالي | المكون الأصل |
| مثيل واحد | نعم، طوال دورة الحياة | لا، يمكن استبداله |
| إعادة الإنشاء عند الرسم | لا، يبقى محفوظًا | يعتمد على الأصل |
| أين يُستخدم | العرض الجذر المالك | طرق العرض الفرعية |
@StateObject يضمن إنشاء الكائن مرة واحدة وبقاءه عبر عمليات التهيئة المتكررة لهيكل العرض. @ObservedObject يستقبل الكائن من الخارج ويتم إعادة إنشائه مع كل تهيئة للهيكل الأصل. إذا كان الأصل يستخدم @StateObject للكائن، يمكن لطرق العرض الفرعية استخدام @ObservedObject بأمان — سيكون الكائن فريدًا عبر التسلسل الهرمي بأكمله.
آلية التتبع لـ @ObservedObject تعتمد على Combine وبروتوكول ObservableObject. أثناء التهيئة، يستدعي SwiftUI publisher objectWillChange — يجب على الكائن إصدار إشارة قبل تغيير خاصية @Published. ينقل Combine الإشارة إلى رسم بياني للتبعيات في SwiftUI، والذي يضع علامة على جميع طرق العرض التابعة على أنها بحاجة إلى تحديث. يحدث هذا بشكل متزامن قبل تغيير القيمة.
class WeatherService: ObservableObject {
@Published var temperature: Double = 22.0
@Published var city: String = "Moscow"
}
struct WeatherView: View {
@ObservedObject var weather: WeatherService
var body: some View {
VStack {
Text("\(weather.city)")
Text("\(weather.temperature)°C")
}
}
}
في المثال WeatherService هو ObservableObject بخاصيتين @Published. WeatherView يعلن @ObservedObject var weather: WeatherService، متلقيًا المثيل من الأصل. عندما تتغير temperature، يتم تشغيل objectWillChange قبل تعيين القيمة الجديدة، يعيد SwiftUI رسم WeatherView، وتظهر درجة الحرارة الفعلية. تتم إدارة الاشتراك تلقائيًا بواسطة SwiftUI — لا يحتاج المطور إلى استدعاء sink أو dispose.
النمط الأول هو تمرير النموذج عبر المُهيئ. ينشئ الأصل ObservableObject عبر @StateObject ويمرره إلى طرق العرض الفرعية كـ @ObservedObject. هذا نقل هرمي قياسي للبيانات حيث يدير العرض الجذر دورة حياة النموذج وتشترك جميع المكونات المتداخلة في التغييرات.
النمط الثاني هو EnvironmentObject، نسخة عامة من @ObservedObject عبر بيئة SwiftUI. يتم حقن الكائن على مستوى المشهد أو العرض الجذر ويكون متاحًا تلقائيًا لجميع المكونات الفرعية دون تمرير صريح عبر المُهيئات. داخل العرض الفرعي، يعمل @EnvironmentObject بشكل مشابه لـ @ObservedObject ولكنه يستقبل الكائن من البيئة.
النمط الثالث هو تكوين عدة ObservableObject. في التطبيقات المعقدة، يمكن للعرض مراقبة عدة كائنات: @ObservedObject var user: UserService, @ObservedObject var network: NetworkMonitor. هذا يفصل المسؤوليات بين الخدمات ويحافظ على قابلية اختبار كل مكون.
struct DashboardView: View {
@ObservedObject var user: UserViewModel
@ObservedObject var network: NetworkMonitor
var body: some View {
VStack {
Text("Welcome, \(user.name)")
HStack {
Circle()
.fill(network.isConnected ? Color.green : Color.red)
.frame(width: 10, height: 10)
}
}
}
}
DashboardView يراقب UserViewModel وNetworkMonitor. كل كائن يتعامل مع مجال بياناته الخاص ويُعلم العرض بشكل مستقل بالتغييرات. إذا انقطعت الشبكة، يغير NetworkMonitor isConnected، ويعيد SwiftUI رسم DashboardView، محدثًا لون المؤشر. تكوين ObservableObject هو الطريقة المفضلة لتنظيم البيانات في تطبيقات SwiftUI.
@Published هو Property Wrapper من Combine يضيف تلقائيًا publisher إلى خاصية داخل ObservableObject. عندما تتغير خاصية @Published، يُنشئ Combine حدثًا عبر publisher objectWillChange. يشترك SwiftUI في هذا publisher عند استخدام @ObservedObject أو @StateObject ويعيد رسم العرض مع كل قيمة جديدة.
@Published يدعم جميع الأنواع، بما في ذلك الاختيارية والمجموعات والهياكل المخصصة. ومع ذلك، بالنسبة للمجموعات (المصفوفات والقواميس)، يتتبع SwiftUI فقط استبدال المرجع، وليس تغيير المحتوى. لاكتشاف إضافة أو إزالة عنصر، تحتاج إلى إعادة تعيين المجموعة بالكامل أو استخدام ObservableObject مع objectWillChange.send() يدوي.
تفصيل مهم: @Published يجب أن يُستخدم فقط داخل class ينفذ ObservableObject. استخدام @Published خارج ObservableObject سيؤدي إلى خطأ في الترجمة. أيضًا، لا يمكن تطبيق @Published على خصائص التهيئة البطيئة (lazy var) أو الخصائص المحسوبة.
الخطأ الأكثر خطورة هو استخدام @ObservedObject لإنشاء كائن. إذا كتبت @ObservedObject var model = UserViewModel() في العرض الأصل، كل رسم سينشئ مثيلًا جديدًا من UserViewModel. سيتم فقدان البيانات وإعادة إنشاء اشتراكات @Published. استخدم دائمًا @StateObject للإنشاء و@ObservedObject فقط لاستقبال كائن جاهز.
الخطأ الثاني هو تعديل خصائص @Published خارج السلسلة الرئيسية. يستخدم ObservableObject Combine الذي يتطلب إرسال التغييرات على السلسلة الرئيسية (main actor). إذا غيرت @Published في قائمة انتظار خلفية، قد يعيد SwiftUI رسم العرض في وقت غير مناسب، مما يسبب حالات سباق. استخدم DispatchQueue.main.async أو @MainActor للتحديثات.
المشكلة الثالثة هي التحديثات الدورية. إذا أدى تغيير @Published إلى آثار جانبية تغير @Published مرة أخرى، يحدث حلقة لا نهائية من إعادة الرسم. الحل: استخدام أعلام حماية (isUpdating) أو فصل المنطق عبر ObservableObject مختلف بحدود مسؤولية واضحة.
الأسئلة الشائعة
نعم، SwiftUI يدعم @ObservedObject var model: UserViewModel?. ومع ذلك، لن تشترك طريقة العرض في التغييرات طالما أن الكائن nil. عند تعيين قيمة، يتم تنشيط الاشتراك تلقائيًا.
@ObservedObject يستقبل الكائن عبر المُهيئ، @EnvironmentObject عبر بيئة SwiftUI. @EnvironmentObject لا يتطلب تمريرًا صريحًا عبر المنشئات، ولكن يجب حقن الكائن على المستوى الأعلى من التسلسل الهرمي.
استدع objectWillChange.send() قبل تغيير الخاصية. هذا مفيد عندما لا يكون @Published مناسبًا (على سبيل المثال، للخصائص المحسوبة أو عمليات المجموعات حيث تحتاج إلى الإبلاغ عن التغيير قبل التعديل).
@ObservedObject و @Published يتتبعان استبدال المرجع، وليس تغيير محتوى المجموعة. لإعادة الرسم، تحتاج إلى إعادة تعيين المصفوفة: items.append(newItem) → items = items أو استخدام objectWillChange.send() قبل التعديل.
لا، @ObservedObject هو Property Wrapper خاص بـ SwiftUI متاح فقط داخل الأنواع التي تنفذ بروتوكول View. للهياكل العادية، استخدم Combine مباشرة مع ObservableObjectPublisher.
الملخص
سنقوم بتطوير تطبيق جوال جاهز
تقدم IT Sectr تطبيقات iOS وAndroid للشركات الناشئة والشركات منذ عام 2017. سوف نقدم لك النصح ونقترح أفضل حل.