Difference between @State, @Binding, @StateObject, @ObservedObject, @Environment and @EnvironmentObject in SwiftUI
ملخص ال property wrappers في SwiftUI بالعربي
مبدإيا قبل ما اتكلم عن الفرق بينهم هتكلم احنا ليه بنحتاجهم
لو جربت تشتغل SwiftUI هتلاقي ان ال views عندك عباره عن structs ومن المعروف ان ال structs بتبقي value type يعني لما بيحصلها تغيير فهو ف الحقيقه بيبقي copy جديده من ال version القديمه لل struct ، يعني SwiftUI مش بتعدل على الـView القديمة، بل بتعمل rebuild لل UI كله بناءً على ال data الجديدة، مع نظام ذكي يقدر يعمل re-render للأجزاء اللي اتغيرت بس.
*انصحك تقرا المقال ده عشان تعرف الفرق بين ال value type وال reference type
ف بالتالي اي قيمة محتفظه بيها طول ماهي جوا scope ال view هيتعملها init تاني لما اعمل rebuild لل view يبقي كده انا محتاج اخزن قيم برا scope ال view عشان تفضل محتفظه بالقيمه بتاعتها عشان تقولي ال state لحاجات معينه جوا ال view
فيجي بقي دور ال property wrappers اللي هنتكلم عنهم، لانهم بيوفروا طريقة لتخزين البيانات خارج نطاق ال View struct نفسها، بحيث البيانات تفضل موجودة حتى لو حصل إعادة بناء لل View.
طيب هل بيتعاد رسم كل الأجزاء؟
لا مش كلها لان SwiftUI بيستخدم حاجة اسمها Diffing System اللي بتعمل مقارنة بين النسخة القديمة والجديدة من ال View، وده بيخليها تحدد الأجزاء اللي فعلاً اتغيرت وتحتاج إعادة رسم. يعني مش كل حاجة بتتعاد من الأول، بس ال elements اللي حصل فيها تغيير هي اللي بتتعاد او بيتعملها rebuild بناءً على البيانات الجديدة.
نبدأ..
خليني اقسمهم الاول ل مجموعتين
١- ال Value Type Wrappers
ودول اللي بعرف بيهم variables بتخزن قيم من نوع value type زي ال int و ال string وال dictionary او object من struct وهما
Wrapper @State
Wrapper @Binding
Wrapper @Environment
٢- ال Reference Type Wrappers
ودول اللي بعرف بيهم variables بتخزن قيم من نوع reference type زي object من class وهما
Wrapper @StateObject
Wrapper @ObservedObject
Wrapper @EnvironmentObject
ملحوظه : لازم ال class يكون بي conform ل ObservableObject عشان اقدر استخدمه مع ال StateObject@ وObservedObject@ واللي علي الاغلب هيكون ال view model بتاعك
نشرح بقي كل property wrapper لوحدها
خليني بس ادي hint
ان the @State and @Binding مرتبطين ببعض
وان the @StateObject and @ObservedObject مرتبطين ببعض
وان the @Environment and @EnvironmentObject مختلفين بس في القيم اللي بيقدرو يحتفظو بيها زي ما قلنا فوق
١- The @State Wrapper
هي property wrapper بتسمحلي اقول ان ال variable ده هيقدر يحتفظ بقيمته حتي لو ال view حصله تغيير واتعمله rebuild يعني بتخليه برا scope ال view وان كمان لو المتغير ده اتغير فال view هيتعمله rebuild بالتغييرات اللي حصلت.
لو عرفت متغير من غيرها وال view اتعمله rebuild ف هيرجع للقيمة الاساسيه وكاني عملتله init من جديد ، وتاني حاجه ان لو المتغير ده حصله اي تعديل فال view مش هيتاثر بيه وهيفضل زي ما هو
هنا count كل ما يتغير ال view هيتعاد رسمها مره تانيه ولانها @state كده هي برا scope ال view ومتخزنه في ال heap يعني بقت refrence type جوا vlaue type اللي هو ال view struct واللي بيتخزن في ال stack فبالتالي بيفضل محتفظ بالقيم حتي لو ال view اتهد وتبني تاني يعني لو مكانش @state كان هيتعمله init ب زيرو كل مره ال view هيتعمله rebuild وفي نفس الوقت count+1 مكانتش هتخلي ال ui يتعمله rebuild فكده @state بتحتفظ بالقيمه وبرضه بت notify ال view انه في تغيير حصل وانه محتاج يترسم بناء علي التغييرات
ملحوظة: مش معني ان المتغير count برا scope ال view انه مش owned by ال view وليه ال lifecycle بتاعتها، يعني لو ال view اتشال تماما كل ال refernce type variables اللي جواه برضه هيتعملها release
٢- The @Binding Wrapper
بتخليني اشارك ال State من View رئيسية ل Sub-Views. الفكرة إن ال @Binding بتخلي الـSub-View تقدر تغير القيمة بدون ما تمتلكها، فهي refrence للقيمة اللي في ال State اللي في ال parent.
هنا ال isOn اللي في ToggleView بتغير ال isOn اللي في ال ParentView ونلاحظ ان وانا ببعتلها المتغير بيكون قبله $ واللي بتسمحلي احطه ك reference
يعني ال State و ال Binding مرتبطين ببعض والاتنين بقدر اخزن فيهم value types
بس ال view اللي بيستخدم the @state بيبقي هو المسؤول عن creation واللي بيستخدم the @Binding بياخد بس reference ليه
٣- The @StateObject
هو بالظبط زي The @State wrapper بس هو بيتعامل مع reference type data
اي تغيير هيحصل لاي Published var جوا ال Counter class ال view هيكون متتبعه وهيعمل rebuild مع التغيير
٤- The @ObservedObject
بتخليني اشارك ال StateObject من View رئيسية ل Sub-Views بالظبط زي The @Binding بس اختلاف القيم اللي بيحفظها واللي بتكون reference type يعني بتستخدمها لو ال view مش مسؤول عن انه ي create ال object ، في view parent تاني هو المسؤول عن كده بس باخد بس reference منه
هنا برضه ال ChildView واخد reference لل StateObject وبيقدر يعدل عليه من غير ما يمتلكه
يعني ال StateObject و ال ObservedObject مرتبطين ببعض والاتنين بقدر اخزن فيهم reference types
بس ال view اللي بيستخدم the @StateObject بيبقي هو المسؤول عن creation ال object واللي بيستخدم the @ObservedObject بياخد بس reference ليه
٥- The @Environment
هنا بنتكلم عن حاجه زي ال general values بس بالنسبه لل views يعني متغير بعرفه في مكان ما واقدر اديه لاي child view بس طبعا بحتاج امرره في مكان ما في upper view عشان اقدر استخدمه في ال lower views
عندي بقي طريقين
اول طريقه: ان استخدم predefined values جوا SwiftUI جوا struct اسمه Environment Values زي مثلا ال colorScheme او layoutDirection او font واللي متضافين لل struct من خلال ال extensions وليهم طبعا default values واقدر اغيرها براحتي وانا ببعتها من ال parent لل childs
@Environment(\.colorScheme) القوسين دول معناهم هات القيمه اللي متسجله لل Environment key ده وبعدها بيحطها للمتغير اللي بعرفه
وتاني طريقة: ان اضيفله انا values تانيه بنفس الطريقه برضه باني اعمل extension ل Environment Values ب key انا اللي بعمله create زي ال CustomeColorKey اللي ف المثال تحت واللي لازم يكون بي conform ل EnvironmentKey واللي بيجبرني اضيف default value وبكده في ال upper view اللي هو ال parent بعت ال environment value لل ContentView
ملحوظة:
ال Binding فوق كانت بتخليني اقدر اشوف values في ال parent واغير عليها انما في ال Environment انا بقدر اشوفها بس ومش بقدر اغير
ال Binding مش بتستخدم ال Environment Values يعني لازم يكون معمول init ل State في ال parent بس ال Environment مش دايما لازم اعمل init لان سواء استخدمت predefined values او انا ضفت key جديد بكون حطاله default value
هنا عرفت Environment Value وبعتها لل subviews من ال parent وكاني بقول اي subview جواك انا بديله القيمه دي ومحتجتهاش في ١ ولا ٢ بس استخدمتها ف ٣ عادي من غير ما احتاج امررها من ١ او ٢ ل ٣
بس ال binding كان لازم ابعته من كل subview للتاني عشان اقدر اربطهم ويكونو متزامنين مع بعض لو حصل تغيير من اي مكان يسمع ف باقي الاماكن
٦- The @EnvironmentObject
بنفس الطريقه بس هنا بنتعامل مع reference value عشان اوصل ل objects من نوع ObservableObject في كل ال view hierarchy من غير ما احتاج من امررها من كل subview لل sub-subview بيكفي امررها بس مره من ال parent من خلال ال environment
بس هنا بحتاج ا create object في ال parent عشان اقدر امرره لان SwiftUI ما بيدعمش @
EnvironmentObject بقيمة افتراضية او default value
يعني هنا احتجت اعرف StateObject اسمه settings في ال parent عشان اقدر امرره في ال environmentObject لكل ال subviews اللي جواه ، مكنتش بحتاج اعمل كده في ال @Environmet
اقدر اضيف برضه key لل Environment Values واستخدمه عادي بس برضه بحتاج اكريت ال object في ال parent عشان احنا هنا بنتعامل مع refernce type فلازم اتاكد انه واخد ال lifecycle بتاعت ال view عشان متحصلش مشكله ف الميموري
ركز ف الاكواد اللي كتباها عشان تقدر تميز الفرق
وبس كده اتمني اكون قدرت اجمعلك الفرق بينهم بشكل بسيط .. وممتنه للفيدباك..
Great job ، Your explanation is clear and impressive keep going
good work 💪