@EnvironmentObject là một property wrapper trong SwiftUI cho phép bất kỳ View nào trong hệ thống phân cấp truy cập vào ObservableObject mà không cần truyền rõ ràng qua một chuỗi các bộ khởi tạo. Đối tượng được tiêm vào môi trường bằng cách sử dụng bộ sửa đổi .environmentObject() tại một cấp độ cụ thể của hệ thống phân cấp, sau đó tất cả View con có thể truy cập nó qua @EnvironmentObject. Điều này loại bỏ nhu cầu truyền đối tượng qua các View trung gian không sử dụng nó — cái gọi là prop drilling. Theo một bài báo của John Sundell — Swift by Sundell (2025), @EnvironmentObject đặc biệt hữu ích cho dữ liệu đa màn hình: phiên người dùng, cài đặt ứng dụng, trình quản lý giỏ hàng hoặc bộ nhớ đệm dữ liệu cục bộ.
Những điểm chính
@EnvironmentObject là một property wrapper cho phép View SwiftUI truy cập ObservableObject từ môi trường của ứng dụng. Môi trường là một vùng chứa có thể đặt các đối tượng ở bất kỳ cấp độ nào của hệ thống phân cấp View bằng cách sử dụng bộ sửa đổi .environmentObject(). Sau khi một đối tượng được đặt vào môi trường, bất kỳ View con nào cũng có thể truy cập nó bằng cách khai báo một thuộc tính với @EnvironmentObject và chỉ định loại đối tượng.
Mục đích chính của @EnvironmentObject là giải quyết vấn đề truyền dữ liệu qua hệ thống phân cấp View sâu mà không cần truyền đối tượng qua mọi cấp trung gian. Trong các ứng dụng phức tạp với NavigationStack, TabView và cửa sổ phương thức phân nhánh, @EnvironmentObject đơn giản hóa đáng kể kiến trúc bằng cách loại bỏ mã boilerplate.
Theo Apple Developer Documentation — Environment (2025), @EnvironmentObject sử dụng cơ chế nội bộ của SwiftUI dựa trên PreferenceKey và nhận dạng View. Mỗi View lưu trữ một tham chiếu đến môi trường riêng của nó, được kế thừa từ View cha và có thể được mở rộng bằng .environmentObject(). Việc tìm kiếm đối tượng đi lên hệ thống phân cấp đến View gốc.
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 hoạt động dựa trên cơ chế tiêm phụ thuộc (DI) được tích hợp trong SwiftUI. Khi bạn gọi .environmentObject() trên một View, SwiftUI lưu trữ đối tượng trong một bộ nhớ đặc biệt được liên kết với View đó và tất cả các phần tử con của nó. Khi một View con khai báo @EnvironmentObject cùng loại, SwiftUI tìm kiếm đối tượng trong môi trường, đi lên hệ thống phân cấp cha.
Một đặc điểm quan trọng — loại đối tượng được sử dụng làm khóa để tìm kiếm trong môi trường. Nếu có hai đối tượng cùng loại trong môi trường, SwiftUI tìm đối tượng gần nhất với View hiện tại trong hệ thống phân cấp. Khi đối tượng được tiêm ở cấp WindowGroup, nó trở nên khả dụng trên toàn cầu cho tất cả các màn hình của ứng dụng, thuận tiện cho các dịch vụ đa năng.
Theo objc.io — SwiftUI Architecture (2025), nội bộ @EnvironmentObject sử dụng cơ chế tương tự như @ObservedObject, nhưng với một lớp trừu tượng bổ sung để tìm đối tượng trong hệ thống phân cấp. SwiftUI không sao chép đối tượng và không tạo đối tượng mới — nó truyền một tham chiếu đến thể hiện hiện có, vì vậy các thay đổi đối với đối tượng sẽ tự động hiển thị cho tất cả View sử dụng @EnvironmentObject.
Cả @EnvironmentObject và @ObservedObject đều thực hiện cùng một chức năng cơ bản — chúng đăng ký View để theo dõi các thay đổi trong ObservableObject. Sự khác biệt nằm ở cơ chế truyền đối tượng. @ObservedObject yêu cầu truyền rõ ràng qua bộ khởi tạo, trong khi @EnvironmentObject lấy đối tượng từ môi trường mà không cần chỉ định rõ ràng trong mỗi View trung gian.
| Đặc điểm | @EnvironmentObject | @ObservedObject |
|---|---|---|
| Truyền | Qua .environmentObject() ở cấp hệ thống phân cấp | Qua bộ khởi tạo của mỗi View |
| Hiển thị phụ thuộc | Ẩn — không hiển thị trong chữ ký View | Rõ ràng — hiển thị trong init của View |
| View trung gian | Không biết về đối tượng | Phải truyền đối tượng tiếp |
| Rủi ro lỗi | Lỗi runtime khi thiếu đối tượng | Kiểm tra lúc biên dịch (nếu tham số bắt buộc) |
| Prop drilling | Loại bỏ | Yêu cầu truyền thủ công |
Lựa chọn giữa @EnvironmentObject và @ObservedObject phụ thuộc vào kiến trúc. Nếu đối tượng cần ở sâu trong hệ thống phân cấp và trên nhiều màn hình — @EnvironmentObject thuận tiện hơn. Nếu kiến trúc yêu cầu chỉ định phụ thuộc rõ ràng để kiểm thử và dễ đọc — @ObservedObject được ưu tiên.
Kịch bản phổ biến nhất là phiên người dùng cần có thể truy cập trên tất cả các màn hình của ứng dụng. Bằng cách tiêm UserSession qua .environmentObject() tại gốc của ứng dụng, bất kỳ màn hình nào cũng có thể truy cập dữ liệu người dùng và trạng thái ủy quyền.
struct ProfileView: View {
@EnvironmentObject var session: UserSession
var body: some View {
VStack {
if session.isLoggedIn {
Text("Hello, \(session.userName)")
Button("Logout") {
session.isLoggedIn = false
}
} else {
LoginView()
}
}
}
}
struct SettingsView: View {
@EnvironmentObject var session: UserSession
var body: some View {
Form {
Text("Logged in as \(session.userName)")
}
}
}
Lưu ý rằng cả ProfileView và SettingsView đều không nhận phiên qua bộ khởi tạo. Chúng chỉ đơn giản khai báo @EnvironmentObject var session: UserSession, và SwiftUI tự động tìm đối tượng trong môi trường. Điều này cho phép thêm màn hình mới mà không cần thay đổi mã truyền dữ liệu hiện có.
Rủi ro chính của @EnvironmentObject là lỗi runtime nếu đối tượng chưa được tiêm vào môi trường. Không giống như các tham số tùy chọn, @EnvironmentObject không thể là nil. Nếu một View với @EnvironmentObject xuất hiện trên màn hình và View cha chưa gọi .environmentObject() cho loại đó, ứng dụng sẽ bị crash ngay lập tức với “Fatal error: No ObservableObject of type X found”.
Nếu bạn tiêm hai đối tượng cùng loại ở các cấp hệ thống phân cấp khác nhau, View con sẽ nhận được đối tượng gần nhất theo hệ thống phân cấp. Điều này có thể gây nhầm lẫn nếu nhà phát triển mong đợi đối tượng từ môi trường gốc có sẵn trong cửa sổ phương thức có môi trường riêng với đối tượng cùng loại.
Với sự phát triển của SwiftUI, các phương pháp quản lý phụ thuộc thay thế đã xuất hiện giải quyết một số nhược điểm của @EnvironmentObject — chủ yếu là tính ẩn của phụ thuộc và rủi ro lỗi runtime.
Lựa chọn cách tiếp cận phụ thuộc vào quy mô nhóm và độ phức tạp của ứng dụng. Đối với các dự án nhỏ, @EnvironmentObject hoạt động rất tốt. Đối với các dự án lớn với hàng chục màn hình và yêu cầu kiểm thử nghiêm ngặt, việc truyền rõ ràng qua @ObservedObject hoặc container DI được ưu tiên.
Câu hỏi thường gặp
Có, một View có thể khai báo bao nhiêu @EnvironmentObject thuộc các loại khác nhau tùy ý. SwiftUI tìm kiếm từng loại độc lập trong môi trường. Điều này thuận tiện khi View cần truy cập đồng thời vào phiên người dùng, cài đặt và giỏ hàng — mỗi đối tượng được tiêm riêng biệt.
Preview sẽ bị crash với lỗi runtime khi cố gắng hiển thị View. Luôn thêm .environmentObject() trong Preview cho các View sử dụng @EnvironmentObject. Sử dụng các đối tượng giả với dữ liệu kiểm thử để Preview hoạt động chính xác và hiển thị trạng thái thực tế.
Không, @EnvironmentObject chỉ hoạt động với loại lớp cụ thể tuân thủ ObservableObject. Đối với giao thức, bạn cần sử dụng type erasure hoặc wrapper: tạo một lớp wrapper chứa tham chiếu đến đối tượng kiểu giao thức và tiêm wrapper qua @EnvironmentObject.
Tạo một thể hiện ObservableObject với dữ liệu kiểm thử và truyền nó cho View qua .environmentObject(testObject) trong kiểm thử. Đây là mẫu tiêu chuẩn cho kiểm thử UI SwiftUI. Đối với kiểm thử đơn vị, hãy tách biệt logic trong ObservableObject và kiểm thử nó riêng biệt với View.
@EnvironmentObject không tạo ra tải hiệu suất bổ sung vì nó chỉ truyền một tham chiếu đến đối tượng, không phải bản sao. Tuy nhiên, việc cập nhật thường xuyên các thuộc tính @Published trong đối tượng toàn cục có thể khiến nhiều View hiển thị lại đồng thời, điều này có thể ảnh hưởng đến hiệu suất.
Tóm tắt
Chúng tôi sẽ phát triển ứng dụng di động chìa khóa trao tay
IT Sectr tạo các ứng dụng iOS và Android cho các công ty khởi nghiệp và doanh nghiệp từ năm 2017. Chúng tôi sẽ tư vấn và đề xuất giải pháp tốt nhất cho bạn.
Đọc thêm