@Environment در SwiftUI — property wrapper برای خواندن مقادیر از محیط سیستم که به طور خودکار در سلسلهمراتب View پخش میشوند. این مؤلفه دسترسی به طرح رنگ، locale، اندازه فونت، managedObjectContext و دهها پارامتر سیستمی دیگر را فراهم میکند. بر اساس Apple Developer Documentation (2025)، @Environment تضمین میکند که هر تغییری در مقدار محیط باعث بازترسیم تمام Viewهای مشترک میشود و بهروزرسانی واکنشگرای رابط را بدون فراخوانی دستی فراهم میکند.
نکات کلیدی
@Environment — یک property wrapper SwiftUI است که برای خواندن مقادیر از محیط سیستم طراحی شده است. محیط یک ظرف سلسلهمراتبی از مقادیر است که SwiftUI به طور خودکار از Viewهای والد به فرزندان منتشر میکند. هر مقدار محیط با یک کلید — نوعی منطبق بر پروتکل EnvironmentKey — شناسایی میشود.
مکانیزم محیط شبیه تزریق وابستگی (dependency injection) در سطح فریمورک است: سیستم مجموعهای از مقادیر از پیش تعریفشده را ارائه میدهد — طرح رنگ (روشن/تاریک)، locale، اندازه فونت، managedObjectContext برای Core Data، dismiss برای بستن صفحه و بسیاری دیگر. Viewای که @Environment را با کلید مشخصی اعلام کرده است، به طور خودکار مقدار فعلی را دریافت میکند و در صورت تغییر آن بازترسیم میشود.
معماری محیط SwiftUI بر اساس پروتکل EnvironmentValues — ساختاری حاوی تمام مقادیر سیستمی — استوار است. هر مقدار به عنوان یک ویژگی از این ساختار با getter و setter ذخیره میشود. @Environment از key path برای دسترسی به ویژگی خاص استفاده میکند: @Environment(\.colorScheme) — دسترسی به طرح رنگ، @Environment(\.locale) — دسترسی به locale.
Property wrapper @Environment دو مکانیزم کلیدی را پیادهسازی میکند: خواندن مقدار از محیط و اشتراک در تغییرات آن. هنگام ایجاد View، SwiftUI از تمام ویژگیهای @Environment عبور میکند و آنها را به مقادیر متناظر از زمینه فعلی متصل میکند. اگر View والد مقدار را از طریق modifier .environment() تغییر دهد، تمام Viewهای فرزندی که این مقدار را میخوانند به طور خودکار بازترسیم میشوند.
ویژگی مهم: @Environment از مقادیر اختیاری پشتیبانی میکند. اگر مقداری در سلسلهمراتب تنظیم نشده باشد، مقدار پیشفرض تعریفشده در EnvironmentKey برگردانده میشود. برای کلیدهای سیستمی، مقدار پیشفرض همیشه منطقی است — برای مثال، طرح رنگ پیشفرض .light. برای کلیدهای سفارشی، برنامهنویس خود مقدار پیشفرض را در متد defaultValue پروتکل EnvironmentKey تعیین میکند.
struct EnvironmentReaderView: View {
@Environment(\.colorScheme) var colorScheme
@Environment(\.locale) var locale
@Environment(\.sizeCategory) var sizeCategory
var body: some View {
VStack {
Text("طرح فعلی: \(colorScheme == .dark ? "Dark" : "Light")")
Text("Locale: \(locale.identifier)")
Text("اندازه فونت: \(sizeCategory)")
}
}
}
در این مثال، View سه مقدار سیستمی محیط را میخواند. هنگام تغییر colorScheme — برای مثال، کاربر حالت تاریک را در تنظیمات فعال کرده است — View به طور خودکار با مقدار جدید بازترسیم میشود. به همین ترتیب هنگام تغییر منطقه یا اندازه فونت (Dynamic Type). View نیازی به اشتراک در اعلانها یا فراخوانی بهروزرسانی ندارد — SwiftUI این کار را به طور خودکار مدیریت میکند.
SwiftUI دهها مقدار سیستمی محیط را ارائه میدهد که جنبههای مختلف رابط و رفتار را پوشش میدهند. طرح رنگ (\.colorScheme) — یکی از پرکاربردترین مقادیر که امکان تطبیق رابط با تم روشن و تاریک را فراهم میکند. Locale (\.locale) شامل تنظیمات منطقهای کاربر برای قالببندی تاریخها، اعداد و ارزها است.
برای Core Data از managedObjectContext (\.managedObjectContext) استفاده میشود — زمینهای که از طریق محیط از persistence container منتقل میشود. برای پیمایش، dismiss (\.dismiss) برای بستن صفحه فعلی و isPresented (\.isPresented) برای نماهای modal در دسترس هستند. برای تقویم و منطقه زمانی — به ترتیب calendar و timeZone.
| Key Path | نوع | کاربرد |
|---|---|---|
| \.colorScheme | ColorScheme | تم روشن یا تاریک |
| \.locale | Locale | تنظیمات منطقهای |
| \.sizeCategory | ContentSizeCategory | اندازه فونت Dynamic Type |
| \.managedObjectContext | NSManagedObjectContext | زمینه Core Data |
| \.dismiss | DismissAction | بستن صفحه |
| \.calendar | Calendar | تقویم فعلی |
| \.timeZone | TimeZone | منطقه زمانی |
| \.horizontalSizeClass | UserInterfaceSizeClass | اندازه افقی صفحه |
برای دسترسی به مقادیر سیستمی، از key path با نقطه استفاده کنید: @Environment(\.dismiss) var dismiss. کامپایلر وجود key path را در EnvironmentValues بررسی میکند، بنابراین کلید اشتباه در مرحله کامپایل خطا ایجاد میکند. مقادیر سیستمی جدید توسط Apple با هر نسخه iOS اضافه میشوند — فهرست فعلی در مستندات EnvironmentValues موجود است.
با وجود نامهای مشابه، @Environment و @EnvironmentObject وظایف متفاوتی را حل میکنند. @Environment مقادیر سیستمی یا سفارشی ثبتشده از طریق EnvironmentKey را میخواند. @EnvironmentObject — یک property wrapper برای ObservableObject است که از طریق محیط بر اساس نوع، بدون کلید صریح، منتقل میشود.
@EnvironmentObject برای تزریق وابستگی استفاده میشود: View والد یک شی (مثلاً ViewModel) ایجاد میکند و آن را از طریق modifier .environmentObject() به Viewهای فرزند منتقل میکند. Viewهای فرزند آن را از طریق @EnvironmentObject دریافت میکنند و میتوانند هم ویژگیهای آن را بخوانند و هم تغییر دهند. @Environment اما — فقط خواندنی برای مقادیر سیستمی است و بازخورد را پشتیبانی نمیکند.
| پارامتر | @Environment | @EnvironmentObject |
|---|---|---|
| کاربرد | مقادیر سیستمی و سفارشی | تزریق ObservableObject |
| کلید | Key path EnvironmentValues | بر اساس نوع شی |
| نوشتن | فقط خواندنی | خواندن و نوشتن |
| مقدار سفارشی | از طریق EnvironmentKey | از طریق کلاس ObservableObject |
| مقدار پیشفرض | دارد (defaultValue) | ندارد (باید ارسال شود) |
در عمل: از @Environment برای دسترسی به پارامترهای سیستمی (تم، locale، اندازه فونت) و تنظیمات سفارشی که در زمان اجرا تغییر نمیکنند استفاده کنید. از @EnvironmentObject برای انتقال ViewModel یا سرویس از طریق سلسلهمراتب View زمانی که نیاز به تغییر وضعیت از مؤلفههای فرزند دارید استفاده کنید.
ایجاد یک مقدار سفارشی محیط را بررسی میکنیم. برای این کار باید ساختاری مطابق با پروتکل EnvironmentKey تعریف کنید و EnvironmentValues را با یک ویژگی جدید گسترش دهید. این امکان را فراهم میکند که تنظیمات تم یا پارامترهای برنامه را بدون props از طریق کل درخت View منتقل کنید.
struct AppThemeKey: EnvironmentKey {
static let defaultValue: AppTheme = .system
}
extension EnvironmentValues {
var appTheme: AppTheme {
get { self[AppThemeKey.self] }
set { self[AppThemeKey.self] = newValue }
}
}
enum AppTheme { case system, light, dark }
پروتکل EnvironmentKey نیاز به پیادهسازی ویژگی ایستا defaultValue دارد — مقداری که اگر View والد محیط سفارشی را تنظیم نکرده باشد استفاده میشود. گسترش EnvironmentValues یک ویژگی محاسبهشده appTheme را با استفاده از subscript با کلید اضافه میکند. پس از آن هر View میتواند مقدار را از طریق @Environment(\.appTheme) بخواند.
struct ThemedView: View {
@Environment(\.appTheme) var appTheme
@Environment(\.colorScheme) var colorScheme
var body: some View {
VStack {
if appTheme == .dark || (appTheme == .system && colorScheme == .dark) {
Text("حالت تاریک فعال")
.foregroundStyle(.white)
.background(Color.black)
} else {
Text("حالت روشن فعال")
.foregroundStyle(.black)
.background(Color.white)
}
}
}
}
struct ContentView: View {
@State private var selectedTheme = AppTheme.system
var body: some View {
ThemedView()
.environment(\.appTheme, selectedTheme)
}
}
ThemedView دو محیط را میخواند: سفارشی appTheme و سیستمی colorScheme. ترکیب امکان پیادهسازی تنظیمات تم انعطافپذیر را فراهم میکند: کاربر میتواند تم «روشن»، «تاریک» یا «سیستمی» را انتخاب کند. اگر سیستمی انتخاب شده باشد — مقدار از colorScheme گرفته میشود که هنگام تغییر تم در تنظیمات iOS به طور خودکار تغییر میکند. View والد (ContentView) مقدار appTheme را از طریق modifier .environment() تنظیم میکند.
struct ModalView: View {
@Environment(\.dismiss) var dismiss
@State private var name = ""
var body: some View {
NavigationStack {
Form {
TextField("Your name", text: $name)
Button("ذخیره") { dismiss() }
}
.navigationTitle("Edit Profile")
}
}
}
این مثال استفاده عملی از dismiss — نمونه DismissAction از محیط را نشان میدهد. فراخوانی dismiss() به عنوان تابع، صفحه modal را میبندد یا NavigationLink را برمیگرداند. تنها شرط این است که View باید به صورت modal ارائه شده باشد یا در داخل NavigationStack قرار داشته باشد. dismiss به طور خودکار از زمینه تعیین میشود: اگر View به صورت sheet باز شده باشد — sheet بسته میشود، اگر به صورت popover — popover بسته میشود.
سوالات متداول
خیر، @Environment فقط برای خواندن طراحی شده است. برای تغییر مقادیر از @EnvironmentObject با ObservableObject یا @Binding استفاده کنید. EnvironmentKeyهای سفارشی میتوانند در گسترش setter داشته باشند، اما تغییر از طریق آن بهروزرسانی UI را فعال نمیکند — این از نظر فنی ممکن است، اما توصیه نمیشود.
@Binding یک اتصال دوطرفه با منبع حقیقت (State, StateObject, ObservableObject) ایجاد میکند. @Environment — خواندن یکطرفه از زمینه سلسلهمراتبی. @Binding برای انتقال داده به View فرزند مناسب است، @Environment — برای دسترسی به تنظیمات سیستمی یا سراسری.
ساختاری را تعریف کنید که پروتکل EnvironmentKey را با static defaultValue پیادهسازی کند. سپس EnvironmentValues را با یک ویژگی با getter/setter از طریق subscript[key] گسترش دهید. پس از ثبت، از @Environment(\.yourKey) برای خواندن و .environment(\.yourKey, value) برای تنظیم استفاده کنید.
SwiftUI بیش از 50 مقدار سیستمی ارائه میدهد: colorScheme, locale, sizeCategory, managedObjectContext, dismiss, calendar, timeZone, horizontalSizeClass, verticalSizeClass, accessibilityEnabled, layoutDirection, legibilityWeight و موارد دیگر. فهرست کامل در مستندات EnvironmentValues.
بله، @Environment در Preview کار میکند، اما مقادیر پیشفرض ممکن است با شبیهساز متفاوت باشد. برای آزمایش در Preview از modifier .environment() مستقیماً در کد Preview استفاده کنید: ThemedView().environment(\.colorScheme, .dark). این امکان بررسی بصری حالتهای مختلف محیط را فراهم میکند.
خلاصه
ما یک اپلیکیشن موبایل به صورت کلید در دست توسعه خواهیم داد
IT Sectr از سال 2017 برنامههای iOS و Android را برای استارتاپها و کسبوکارها ایجاد میکند. ما به شما مشاوره میدهیم و بهترین راهحل را پیشنهاد خواهیم کرد.
همچنین بخوانید