@EnvironmentObject — این چیست، اصل کار و استفاده

نویسنده: IT Sectr منتشر شده: 2026-06-19 زمان مطالعه: 8 دقیقه

@EnvironmentObject — property wrapper در SwiftUI است که به‌طور خودکار ObservableObject را از طریق کل سلسله‌مراتب نمایش‌ها بدون ارسال صریح در اولیه‌ساز انتقال می‌دهد. view زیری با طریق اعلام سادهٔ ویژگی به شیء موجود در محیط دسترسی پیدا می‌کند و والد آن را از طریق روش .environmentObject() فراهم می‌کند. به گزارش Apple Developer Documentation (2025)، SwiftUI از مکانیسم تزریق وابستگی در سطح محیط استفاده می‌کند که نیاز به ارسال داده‌ها از طریق سازنده‌های ویو های میانی را برطرف می‌کند. @EnvironmentObject خصوصاً برای شیئی که در بسیاری از صفحات برنامه مورد نیاز هستند مفید است — مدل‌های احراز هویت، سبد‌های خرید یا تنظیمات سراسری.

نکات کلیدی

  • @EnvironmentObject — property wrapper که ObservableObject را از محیط SwiftUI بدون ارسال از طریق اولیه‌ساز دریافت می‌کند
  • تزریق با روش .environmentObject() در view والد انجام می‌شود — شیء برای تمامی عناصر زیری دسترس می‌شود
  • تفاوت از @ObservedObject: viewهای زیری در اولیه‌ساز پارامتر ندارند، شیء به‌طور خودکار بر اساس نوع گرفته می‌شود
  • خطا نبود شیئی در محیط — کراش برنامه با fatal error، بنابراین شیء باید قبل از اولین view زیری تضمین شود
  • iOS 17+ ماکروی @Observable قسمتاً جایگزین ObservableObject می‌شود، اما @EnvironmentObject با ماکروی جدید از طریق @Environment به کار ادامه می‌دهد

@EnvironmentObject چیست؟

@EnvironmentObject — property wrapperی است که در چارچوب SwiftUI اعلام شده و به view اجازه می‌دهد به شیئی که در محیط ذخیره شده دسترسی پیدا کند. بر خلاف @State یا @StateObject، @EnvironmentObject شیئی ایجاد نمی‌کند — آن فقط یک نمونه موجود را که توسط یکی از اجداد در سلسله‌مراتب view ارائه شده می‌خواند.

مکانیسم کار بر اساس محیط SwiftUI — یک فرهنگ ضمنی که از view ریشه به تمامی زیری‌ها منتقل می‌شود است. وقتی والد روش .environmentObject(someObject) را فراخوان می‌کند، SwiftUI ارجاعی به someObject را در محیط قرار می‌دهد. هر view در زیردرخت می‌تواند @EnvironmentObject var model: ViewModel را اعلام کند و همان نمونه را دریافت کند.

به گزارش جلسه «Demystify SwiftUI» از Apple WWDC 2021، محیط برای انتقال داده‌ها از طریق سلسله‌مراتب عمیق بدون از دست دادن کارایی بهینه‌سازی شده است — دسترسی به شیء از طریق جستجوی بر اساس نوع در O(1) انجام می‌شود. این با ارسال دستی از طریق سازنده‌ها تضاد دارد که پیچیدگی آن با عمق سلسله‌مراتب به‌طور خطی افزایش می‌یابد.

از @EnvironmentObject برای وضعیت سراسری که در سطوح مختلف برنامه مورد نیاز است استفاده کنید. کاندیدای های تیپیکی — مدل‌های احراز هویت، مدیران ناوبری، سبد‌های خرید و تأمین‌کنندگان داده از شبکه.

@EnvironmentObject چگونه کار می‌کند

@EnvironmentObject از مکانیسم SwiftUI به نام تزریق وابستگی مبتنی بر محیط استفاده می‌کند. وقتی SwiftUI سلسله‌مراتب را رندر می‌کند، یک فرهنگ داخلی EnvironmentValues را نگه می‌دارد که در هر سطح قابل خواندن و نوشتن است. Property wrapper @EnvironmentObject که از پروتکل ObservableObject برای مشترک شدن در تغییرات استفاده می‌کند، شیء را از این فرهنگ بر اساس نوع آن می‌خواند.

فرآیند شامل سه مرحله است. اول — ایجاد ObservableObject در جایی در سلسله‌مراتب، معمولاً از طریق @StateObject یا @ObservedObject در view والد. دوم — فراخوانی .environmentObject(object) در این view که شیء را در محیط قرار می‌دهد. سوم — اعلام @EnvironmentObject در viewهای زیری که خودکار همان نمونه را دریافت و مشترک می‌شوند.

SwiftUI تضمین می‌کند که با هر تغییر در هر ویژگی @Published داخل شیء، تمامی viewهای که @EnvironmentObject را با این نوع اعلام کرده‌اند مجدداً رندر می‌شوند. به گزارش دون والز (Donny Wals, 2024)، مکانیسم مشترک‌شدن با @ObservedObject یکسان است — تفاوت فقط در روش دریافت نمونه است، نه در مکانیسم به‌روزرسانی.

سلسله‌مراتب را به گونه‌ای طراحی کنید که شیء تا حد امکان در بالاترین سطح ارائه شود — این دسترسی برای همه viewهایی که به آن نیاز دارند بدون تکرار کد فراهم می‌کند.

@EnvironmentObject در مقابل @ObservedObject

هر دو property wrapper — @EnvironmentObject و @ObservedObject — در ObservableObject مشترک می‌شوند و view را در تغییرات مجدداً رندر می‌کنند. تفاوت کلیدی در روش دریافت شیء است. @ObservedObject نیازمند ارسال صریح نمونه از طریق اولیه‌ساز view است، در حالی که @EnvironmentObject آن را به‌طور خودکار از محیط دریافت می‌کند.

سلسله‌مراتب سه سطحی را در نظر بگیرید: ParentView → MiddleView → ChildView. اگر ChildView به شیء UserSettings نیاز داشته باشد، در صورت استفاده از @ObservedObject باید آن را از طریق MiddleView ارسال کنید، حتی اگر MiddleView از این شیء استفاده نکند:

swift
struct MiddleView: View {
    @ObservedObject var settings: UserSettings  // فقط برای ارسال به پایین مورد نیاز است

    var body: some View {
        ChildView(settings: settings)
    }
}

با @EnvironmentObject، MiddleView نیازی به دانستن وجود شیء ندارد:

swift
struct MiddleView: View {
    var body: some View {
        ChildView()
    }
}

struct ChildView: View {
    @EnvironmentObject var settings: UserSettings

    var body: some View {
        Text(settings.username)
    }
}

به گزارش Swift by Sundell (2024)، @EnvironmentObject زمانی ترجیح دارد که شیء در چند سطح سلسله‌مراتب مورد نیاز باشد، و @ObservedObject زمانی که شیء مستقیماً از والد به تنها فرزند مستقیم ارسال می‌شود. @ObservedObject را برای ارسال‌های محلی و یکباره و @EnvironmentObject را برای وابستگی‌های سراسری انتخاب کنید.

@EnvironmentObject در مقابل @Environment

@Environment و @EnvironmentObject — هر دو داده‌ها را از محیط SwiftUI می‌خوانند، اما با منابع مختلفی کار می‌کنند. @Environment مقادیر ساخته شده یا سفارشی را از EnvironmentValues می‌خواند — اینها داده‌های ساده‌ای هستند: رنگ‌ها، فونت‌ها، اندازه‌ها، تقویم، layoutDirection. @EnvironmentObject انواع ارجاعی مطابق با ObservableObject را می‌خواند.

تفاوت کلیدی — مکانیسم به‌روزرسانی. @Environment از publish-subscribe در سطح مقادیر فردی استفاده می‌کند: با تغییر محیط، تنها viewهایی که آن مقدار را می‌خوانند مجدداً رندر می‌شوند. @EnvironmentObject در objectWillChange ObservableObject مشترک می‌شود که می‌تواند باعث رندر مجدد تمامی viewهای مشترک شده در این نوع شود، صنظر از اینکه کدام ویژگی تغییر کرده است.

به گزارش Hacking with Swift (پاول هادسون، 2025)، @Environment برای پارامترهای پیکربندی مناسب است: طرح رنگ، اندازه قلم پویا، جهت‌گیری دستگاه. @EnvironmentObject — برای منطق کسب و کار و وضعیت: مدل‌های داده، سرویس‌ها، مدیران. از @Environment برای پارامترهای استاتیک یا به ندرت متغیر و @EnvironmentObject برای داده‌های پویایی که نیازمند واکنش‌گرایی هستند استفاده کنید.

در عمل، این دو مکانیسم اغلب با هم ترکیب می‌شوند: @EnvironmentObject داده‌ها را تأمین می‌کند و @Environment زمینه نمایش را فراهم می‌کند.

خطاهای رایج در استفاده از @EnvironmentObject

رایج‌ترین خطا — نبود شیء در محیط در زمان دسترسی به آن. اگر view @EnvironmentObject var model: ViewModel را اعلام کرده است، اما هیچ اجدادی .environmentObject(model) را فراخوان نکرده است، SwiftUI یک fatal error با پیام «No ObservableObject of type ViewModel found» صادر خواهد کرد. این در مرحله رندر اتفاق می‌افتد، نه کامپایل، بنابراین خطا فقط در زمان اجرا ظاهر می‌شود.

دومین مشکل رایج — چند نمونه از یک نوع. SwiftUI از نوع شیء به عنوان کلید برای جستجو در محیط استفاده می‌کند. اگر دو اجداد مختلف نمونه‌های مختلف ViewModel را از طریق .environmentObject ارائه کرده‌اند، view زیری نزدیکترین را در سلسله‌مراتب دریافت می‌کند که می‌تواند منجر به رفتار غیرمنتظره شود. راه حل — طراحی به گونه‌ای که هر نوع دقیقاً یک بار در محیط وجود داشته باشد.

سومین خطا — استفاده از حد @EnvironmentObject برای داده‌هایی که فقط یک یا دو view نیاز دارند. در این حالت، @ObservedObject با ارسال صریح از طریق اولیه‌ساز جریان داده شفاف‌تری ایجاد می‌کند و آزمایش را آسان‌تر می‌کند. به گزارش Point-Free (2025)، تعداد زیاد اشیاء در محیط درک وابستگی‌های view را دشوار می‌کند و کد را کمتر قابل پیش‌بینی می‌کند.

بررسی کنید که هر @EnvironmentObject در سطح مناسب سلسله‌مراتب ارائه شده است و برای شیئی که برای کاهش پیش‌رسی در مرحله اولیه حائز اهمیت هستند، بررسی‌های fallback را در onAppear اضافه کنید.

نمونه کدهای @EnvironmentObject

یک نمونه کامل از یک برنامه با وضعیت سراسری احراز هویت را در نظر بگیرید. یک ObservableObject AuthManager ایجاد می‌کنیم که وضعیت ورود کاربر را ذخیره می‌کند و آن را از طریق @EnvironmentObject در اختیار تمام صفحات قرار می‌دهیم:

swift
import SwiftUI
import Combine

class AuthManager: ObservableObject {
    @Published var isLoggedIn = false
    @Published var username: String = ""

    func login(user: String) {
        username = user
        isLoggedIn = true
    }

    func logout() {
        username = ""
        isLoggedIn = false
    }
}

view ریشه AuthManager را از طریق محیط ارائه می‌دهد:

swift
@main
struct MyApp: App {
    @StateObject private var authManager = AuthManager()

    var body: some Scene {
        WindowGroup {
            ContentView()
                .environmentObject(authManager)
        }
    }
}

view زیری AuthManager را بدون ارسال صریح دریافت می‌کند:

swift
struct ProfileView: View {
    @EnvironmentObject var authManager: AuthManager

    var body: some View {
        VStack {
            if authManager.isLoggedIn {
                Text("Hello, \(authManager.username)")
                Button("خروج") {
                    authManager.logout()
                }
            } else {
                Button("ورود") {
                    authManager.login(user: "user")
                }
            }
        }
    }
}

نمونه سوم — با چند ObservableObject و ترکیب @EnvironmentObject و @Environment. فرض کنید برنامه از CartManager برای سبد خرید و ThemeManager برای طرح رنگ استفاده می‌کند. هر دو در بالاترین سطح ارائه شده و بدون ارسال از طریق اولیه‌سازها در هر صفحه دسترس هستند. این خصوصاً در صفحات با تودریق عمیق یا استفاده از نمایش‌های modal که ارسال داده از طریق سازنده به صورت فنی دشوار است مفید است.

سؤالات متداول

@EnvironmentObject چه تفاوتی با @ObservedObject دارد؟

@ObservedObject نیازمند ارسال صریح نمونه از طریق اولیه‌ساز view است، در حالی که @EnvironmentObject شیء را به‌طور خودکار از محیط SwiftUI دریافت می‌کند. @EnvironmentObject برای داده‌هایی که در چند سطح سلسله‌مراتب مورد نیاز هستند مناسب است، در حالی که @ObservedObject برای ارسال مستقیم بین والد و فرزند ترجیح دارد.

اگر @EnvironmentObject ارائه نشود چه اتفاقی می‌افتد؟

SwiftUI یک fatal error در زمان اجرا صادر می‌کند: «No ObservableObject of type X found». خطا در لحظه رندر viewی که @EnvironmentObject را اعلام کرده اتفاق می‌افتد، اگر هیچ اجدادی .environmentObject() را با شیئی از این نوع فراخوان نکرده باشد. کامپایلر در مورد این وضعیت هشدار نمی‌دهد.

آیا می‌توان @EnvironmentObject را با iOS 13 استفاده کرد؟

بله، @EnvironmentObject از iOS 13.0، macOS 10.15، tvOS 13.0 و watchOS 6.0 قابل دسترسی است. این یکی از اولین property wrapper‌هایی است که توسط Apple همراه با عرضه SwiftUI در سال 2019 معرفی شد و در همه نسخه‌های بعدی از جمله iOS 17 و 18 با ماکروی @Observable کار می‌کند.

چند شیء می‌توان از طریق @EnvironmentObject ارسال کرد؟

تعداد شیئی محدود نیست — هر نوع به عنوان یک کلید منحصر به فرد عمل می‌کند. می‌توان AuthManager، CartManager، NavigationManager و سایر سرویس‌ها را با فراخوانی جداگانه .environmentObject() برای هر کدام ارسال کرد. مهم است که در محیط دو شیء از یک نوع وجود نداشته باشد — این منجر به رفتار نامشخص می‌شود.

چگونه view را با @EnvironmentObject آزمایش کنیم؟

در آزمایش‌ها یک نمونه ObservableObject ایجاد کرده و آن را از طریق .environmentObject(obj) در Preview Provider یا XCTest ارسال کنید. برای آزمایش‌های واحد view، استفاده از پروتکل به جای کلاس مشخص راحت است — این امکان جایگزینی وابستگی‌ها با شیئی mock را بدون تغییر سلسله‌مراتب واقعی فراهم می‌کند.

نتیجه‌گیری

  • @EnvironmentObject — property wrapper برای دریافت خودکار ObservableObject از محیط SwiftUI بدون ارسال از طریق اولیه‌ساز
  • مکانیسم بر اساس تزریق وابستگی مبتنی بر محیط است: شیء با روش .environmentObject() در محیط قرار می‌گیرد و بر اساس نوع بازیابی می‌شود
  • تفاوت با @ObservedObject: @EnvironmentObject viewهای میانی را از نیاز به دانستن وابستگی‌های فرزندان عمیق بی‌نیاز می‌کند
  • تفاوت با @Environment: @EnvironmentObject با ObservableObject کار می‌کند، @Environment — با مقادیر EnvironmentValues
  • ریسک‌ها: fatal error در صورت نبود شیء در محیط، مشکل چند نمونه از یک نوع، سوءاستفاده از وضعیت سراسری
  • iOS 17+ ماکروی @Observable @EnvironmentObject را لغو نمی‌کند — هر دو مکانیسم برای سناریوهای مختلف وجود دارند
  • Best practice: شیئی را در بالاترین سطح سلسله‌مراتب ارائه دهید، از @EnvironmentObject برای سرویس‌های سراسری و @ObservedObject برای ارسال‌های محلی استفاده کنید

ما یک اپلیکیشن موبایل به صورت کلید در دست توسعه خواهیم داد

IT Sectr از سال 2017 برنامه‌های iOS و Android را برای استارتاپ‌ها و کسب‌وکارها ایجاد می‌کند. ما به شما مشاوره می‌دهیم و بهترین راه‌حل را پیشنهاد خواهیم کرد.

بحث درباره پروژه

همچنین بخوانید