@EnvironmentObject — یک property wrapper در SwiftUI است که به هر View در سلسلهمراتب اجازه میدهد بدون ارسال صریح از طریق زنجیره مقداردهی اولیه به ObservableObject دسترسی پیدا کند. شیء با استفاده از اصلاحکننده .environmentObject() در سطح مشخصی از سلسلهمراتب به محیط تزریق میشود و پس از آن همه Viewهای فرزند میتوانند آن را از طریق @EnvironmentObject دریافت کنند. این کار نیاز به ارسال شیء از طریق Viewهای میانی که از آن استفاده نمیکنند را از بین میبرد — به اصطلاح prop drilling. بر اساس مقاله John Sundell — Swift by Sundell (2025)، @EnvironmentObject به ویژه برای دادههای بین صفحهای مفید است: نشست کاربر، تنظیمات برنامه، مدیریت سبد خرید یا حافظه نهان محلی دادهها.
نکات اصلی
@EnvironmentObject — یک property wrapper است که به SwiftUI View اجازه میدهد از محیط (environment) برنامه به ObservableObject دسترسی پیدا کند. محیط یک ظرف است که میتوانید اشیاء را با استفاده از اصلاحکننده .environmentObject() در هر سطحی از سلسلهمراتب View در آن قرار دهید. پس از قرار دادن شیء در محیط، هر View فرزند میتواند با اعلام یک ویژگی با @EnvironmentObject و مشخص کردن نوع شیء به آن دسترسی پیدا کند.
وظیفه اصلی @EnvironmentObject — حل مشکل ارسال دادهها از طریق سلسلهمراتب عمیق View بدون نیاز به ارسال شیء از هر سطح میانی است. در برنامههای پیچیده با ساختار شاخهای NavigationStack، TabView و پنجرههای مدال، @EnvironmentObject معماری را با حذف کدهای تکراری به طور قابل توجهی ساده میکند.
بر اساس Apple Developer Documentation — Environment (2025)، @EnvironmentObject از مکانیزم داخلی SwiftUI مبتنی بر PreferenceKey و شناسایی View استفاده میکند. هر View یک ارجاع به محیط خود ذخیره میکند که از View والد به ارث میرسد و میتواند با .environmentObject() گسترش یابد. جستجوی شیء در سلسلهمراتب به سمت بالا تا View ریشه انجام میشود.
class UserSession: ObservableObject {
@Published var isLoggedIn = false
@Published var userName: String = ""
func login(name: String) {
userName = name
isLoggedIn = true
}
}
@main
struct MyApp: App {
@StateObject var session = UserSession()
var body: some Scene {
WindowGroup {
ContentView()
.environmentObject(session)
}
}
}
@EnvironmentObject بر اساس مکانیزم تزریق وابستگی (DI) تعبیهشده در SwiftUI کار میکند. هنگامی که .environmentObject() را روی یک View فراخوانی میکنید، SwiftUI شیء را در ذخیرهگاه خاصی مرتبط با این View و همه فرزندانش ذخیره میکند. وقتی یک View فرزند @EnvironmentObject از همان نوع را اعلام میکند، SwiftUI با بالا رفتن از سلسلهمراتب والدین، شیء را در محیط جستجو میکند.
ویژگی مهم — نوع شیء به عنوان کلید جستجو در محیط استفاده میشود. اگر دو شیء از یک نوع در محیط وجود داشته باشند، SwiftUI نزدیکترین را به View فعلی در سلسلهمراتب پیدا خواهد کرد. با تزریق شیء در سطح WindowGroup، برای همه صفحههای برنامه به صورت سراسری قابل دسترسی میشود که برای سرویسهای عمومی مناسب است.
بر اساس objc.io — SwiftUI Architecture (2025)، در داخل @EnvironmentObject از مکانیزم مشابه @ObservedObject استفاده میکند، اما با سطح اضافی انتزاع برای جستجوی شیء در سلسلهمراتب. SwiftUI شیء را کپی یا ایجاد نمیکند — ارجاعی به نمونه موجود ارسال میکند، بنابراین تغییرات در شیء به طور خودکار برای همه Viewهای استفادهکننده از @EnvironmentObject قابل مشاهده است.
هم @EnvironmentObject و هم @ObservedObject یک عملکرد اساسی را انجام میدهند — Viewها را در تغییرات ObservableObject مشترک میکنند. تفاوت در مکانیزم ارسال شیء است. @ObservedObject نیاز به ارسال صریح از طریق مقداردهی اولیه دارد، در حالی که @EnvironmentObject شیء را از محیط بدون مشخص کردن صریح در هر View میانی دریافت میکند.
| ویژگی | @EnvironmentObject | @ObservedObject |
|---|---|---|
| ارسال | از طریق .environmentObject() در سطح سلسلهمراتب | از طریق مقداردهی اولیه هر View |
| آشکار بودن وابستگیها | پنهان — در امضای View قابل مشاهده نیست | آشکار — در init View قابل مشاهده است |
| Viewهای میانی | درباره شیء نمیدانند | باید شیء را جلوتر ارسال کنند |
| ریسک خطا | Runtime crash در صورت نبود شیء | بررسی زمان کامپایل (اگر پارامتر اجباری باشد) |
| Prop drilling | حذف میکند | نیاز به ارسال دستی دارد |
انتخاب بین @EnvironmentObject و @ObservedObject به معماری بستگی دارد. اگر شیء در عمق سلسلهمراتب و برای بسیاری از صفحهها مورد نیاز است — @EnvironmentObject راحتتر است. اگر معماری نیاز به مشخص کردن صریح وابستگیها برای آزمایش و خوانایی دارد — @ObservedObject ترجیح داده میشود.
رایجترین سناریو — نشست کاربر که باید در همه صفحههای برنامه قابل دسترسی باشد. با تزریق UserSession از طریق .environmentObject() در ریشه برنامه، هر صفحهای میتواند به دادههای کاربر و وضعیت احراز هویت دسترسی پیدا کند.
struct ProfileView: View {
@EnvironmentObject var session: UserSession
var body: some View {
VStack {
if session.isLoggedIn {
Text("سلام، \(session.userName)")
Button("خروج") {
session.isLoggedIn = false
}
} else {
LoginView()
}
}
}
}
struct SettingsView: View {
@EnvironmentObject var session: UserSession
var body: some View {
Form {
Text("ورود به عنوان \(session.userName)")
}
}
}
توجه کنید: نه ProfileView و نه SettingsView session را از طریق مقداردهی اولیه دریافت نمیکنند. آنها به سادگی @EnvironmentObject var session: UserSession را اعلام میکنند و SwiftUI به طور خودکار شیء را در محیط پیدا میکند. این امکان را میدهد که صفحههای جدید بدون تغییر کد موجود ارسال داده اضافه کنید.
خطر اصلی @EnvironmentObject — runtime crash اگر شیء به محیط تزریق نشده باشد. بر خلاف پارامترهای اختیاری، @EnvironmentObject نمیتواند nil باشد. اگر View با @EnvironmentObject روی صفحه ظاهر شود و View والد برای این نوع .environmentObject() را فراخوانی نکرده باشد، برنامه بلافاصله با خطای «Fatal error: No ObservableObject of type X found» سقوط میکند.
اگر دو شیء از یک نوع را در سطوح مختلف سلسلهمراتب تزریق کنید، View فرزند نزدیکترین را در سلسلهمراتب دریافت میکند. این میتواند منجر به سردرگمی شود اگر توسعهدهنده انتظار داشته باشد که شیء از محیط ریشه در پنجره مدالی که محیط خود را با شیء همنوع دارد قابل دسترسی باشد.
با توسعه SwiftUI، روشهای جایگزین مدیریت وابستگی ظاهر شدهاند که برخی از کاستیهای @EnvironmentObject را برطرف میکنند — در درجه اول پنهان بودن وابستگیها و خطر runtime crash.
انتخاب رویکرد به اندازه تیم و پیچیدگی برنامه بستگی دارد. برای پروژههای کوچک @EnvironmentObject عالی کار میکند. برای پروژههای بزرگ با دهها صفحه و الزامات سختگیرانه آزمایش، ارسال صریح از طریق @ObservedObject یا ظرف DI ترجیح داده میشود.
سؤالات متداول
بله، View میتواند هر تعداد @EnvironmentObject از انواع مختلف را اعلام کند. SwiftUI هر نوع را به طور مستقل در محیط جستجو میکند. این زمانی مفید است که View به طور همزمان به نشست کاربر، تنظیمات و سبد خرید نیاز داشته باشد — هر شیء به طور جداگانه تزریق میشود.
Preview با runtime error سقوط میکند هنگام تلاش برای نمایش View. همیشه برای Viewهایی که از @EnvironmentObject استفاده میکنند در Preview .environmentObject() اضافه کنید. از اشیاء mock با دادههای آزمایشی استفاده کنید تا Preview به درستی کار کند و وضعیت واقعینمایی را نشان دهد.
خیر، @EnvironmentObject فقط با نوع مشخصی از کلاس که ObservableObject را پیادهسازی میکند کار میکند. برای پروتکلها باید از type erasure یا یک wrapper استفاده کنید: یک کلاس wrapper ایجاد کنید که ارجاعی به شیء از نوع پروتکل را ذخیره میکند و wrapper را از طریق @EnvironmentObject تزریق کنید.
یک نمونه ObservableObject با دادههای آزمایشی ایجاد کنید و آن را از طریق .environmentObject(testObject) در تست به View ارسال کنید. این الگوی استاندارد برای تست UI SwiftUI است. برای تستهای واحد، منطق را در ObservableObject جدا کنید و آن را مستقل از View تست کنید.
@EnvironmentObject بار اضافی بر عملکرد ایجاد نمیکند، زیرا فقط ارجاع را به شیء ارسال میکند و آن را کپی نمیکند. با این حال، بهروزرسانی مکرر ویژگیهای @Published در شیء سراسری میتواند باعث بازترسیم همزمان بسیاری از Viewها شود که ممکن است بر عملکرد تأثیر بگذارد.
خلاصه
ما یک اپلیکیشن موبایل به صورت کلید در دست توسعه خواهیم داد
IT Sectr از سال 2017 برنامههای iOS و Android را برای استارتاپها و کسبوکارها ایجاد میکند. ما به شما مشاوره میدهیم و بهترین راهحل را پیشنهاد خواهیم کرد.
همچنین بخوانید