Bỏ qua để đến nội dung

@EnvironmentObject

@EnvironmentObject chia sẻ data xuyên suốt View hierarchy.

class AppSettings: ObservableObject {
@Published var isDarkMode = false
@Published var language = "en"
}
@main
struct MyApp: App {
@StateObject var settings = AppSettings()
var body: some Scene {
WindowGroup {
ContentView()
.environmentObject(settings)
}
}
}
ContentView()
.environmentObject(settings)
struct ChildView: View {
@EnvironmentObject var settings: AppSettings
var body: some View {
Text("Dark mode: \(settings.isDarkMode ? "On" : "Off")")
}
}
class ThemeManager: ObservableObject {
@Published var isDarkMode = false
@Published var accentColor = Color.blue
var backgroundColor: Color {
isDarkMode ? .black : .white
}
var textColor: Color {
isDarkMode ? .white : .black
}
}
struct RootView: View {
@StateObject var themeManager = ThemeManager()
var body: some View {
TabView {
HomeView()
SettingsView()
}
.environmentObject(themeManager)
.preferredColorScheme(themeManager.isDarkMode ? .dark : .light)
}
}
struct HomeView: View {
@EnvironmentObject var theme: ThemeManager
var body: some View {
VStack {
Text("Home")
.foregroundColor(theme.textColor)
}
.background(theme.backgroundColor)
}
}
struct SettingsView: View {
@EnvironmentObject var theme: ThemeManager
var body: some View {
Form {
Toggle("Dark Mode", isOn: $theme.isDarkMode)
ColorPicker("Accent Color", selection: $theme.accentColor)
}
}
}
class UserSession: ObservableObject {
@Published var currentUser: User?
@Published var isAuthenticated = false
func login(email: String, password: String) async throws {
// API call
currentUser = User(name: "John", email: email)
isAuthenticated = true
}
func logout() {
currentUser = nil
isAuthenticated = false
}
}
struct AppRootView: View {
@StateObject var session = UserSession()
var body: some View {
Group {
if session.isAuthenticated {
MainTabView()
} else {
LoginView()
}
}
.environmentObject(session)
}
}
struct ProfileView: View {
@EnvironmentObject var session: UserSession
var body: some View {
VStack {
if let user = session.currentUser {
Text("Welcome, \(user.name)")
}
Button("Logout") {
session.logout()
}
}
}
}
class CartManager: ObservableObject {
@Published var items: [CartItem] = []
var total: Double {
items.reduce(0) { $0 + $1.price * Double($1.quantity) }
}
var itemCount: Int {
items.reduce(0) { $0 + $1.quantity }
}
func add(_ product: Product) {
if let index = items.firstIndex(where: { $0.productId == product.id }) {
items[index].quantity += 1
} else {
items.append(CartItem(productId: product.id, name: product.name, price: product.price, quantity: 1))
}
}
func remove(_ item: CartItem) {
items.removeAll { $0.id == item.id }
}
}
struct ProductCard: View {
let product: Product
@EnvironmentObject var cart: CartManager
var body: some View {
VStack {
Text(product.name)
Text("$\(product.price, specifier: "%.2f")")
Button("Add to Cart") {
cart.add(product)
}
.buttonStyle(.borderedProminent)
}
}
}
struct CartBadge: View {
@EnvironmentObject var cart: CartManager
var body: some View {
ZStack(alignment: .topTrailing) {
Image(systemName: "cart")
if cart.itemCount > 0 {
Text("\(cart.itemCount)")
.font(.caption2)
.foregroundColor(.white)
.padding(4)
.background(Circle().fill(.red))
.offset(x: 8, y: -8)
}
}
}
}
struct App: App {
@StateObject var userSession = UserSession()
@StateObject var settings = AppSettings()
@StateObject var cart = CartManager()
var body: some Scene {
WindowGroup {
ContentView()
.environmentObject(userSession)
.environmentObject(settings)
.environmentObject(cart)
}
}
}
#Preview {
ProductCard(product: Product(name: "iPhone", price: 999))
.environmentObject(CartManager())
}
Feature @StateObject @EnvironmentObject
Tạo object
Pass qua init
Inject - .environmentObject()
Scope View đó Toàn bộ subtree

  • Inject với .environmentObject(object)
  • Access với @EnvironmentObject var obj: Type
  • Dùng cho global state: theme, user session, cart
  • Phải inject trước khi access (crash nếu không có)
  • Dùng chung reference, thay đổi affect tất cả views