SwiftUI报错:未找到GetData类型ObservableObject的解决求助
问题:Fatal error: No ObservableObject of type GetData found
在AppView中遇到致命错误:
Fatal error: No ObservableObject of type GetData found. A View.environmentObject(_:) for GetData may be missing as an ancestor of this view.
已尝试在AppView()和ContentView()中添加.environmentObject(GetData()),同时在AppView的TabView以及ContentView的body中添加.environmentObject(gd),但问题仍未解决。
相关代码
FinalProjectApp.swift
import SwiftUI import SwiftData import Firebase import FirebaseAuth @main struct FinalProjectApp: App { @StateObject var data = AllDrinks() @StateObject var gd = GetData() init(){ FirebaseApp.configure() } var body: some Scene { WindowGroup { if Auth.auth().currentUser != nil { AppView() .environmentObject(data) .environmentObject(gd) } else { ContentView() .environmentObject(data) .environmentObject(gd) } } } }
AppView.swift
import SwiftUI import FirebaseFirestore import FirebaseFirestoreSwift import FirebaseAuth struct AppView: View{ @EnvironmentObject var data: AllDrinks @EnvironmentObject var gd: GetData @State var name: String? init() { UITabBar.appearance().backgroundColor = UIColor.wootea if let user = Auth.auth().currentUser{ gd.user_name = user.displayName ?? "" print("AppView name:\(gd.user_name)") } } var body: some View{ TabView{ HomeView() .tabItem{ Label("", systemImage: "house") } // FavoriteView() // .tabItem{ // Label("", systemImage: "heart") // } // OrderView() // .tabItem{ // Label("", systemImage: "cart") // } // AccountView() // .tabItem{ // Label("", systemImage: "person") // } } .tabViewStyle(DefaultTabViewStyle()) .environmentObject(data) .environmentObject(gd) } } struct AppView_Previews: PreviewProvider { static var previews: some View { AppView() .environmentObject(AllDrinks()) .environmentObject(GetData()) } }
ContentView.swift
import SwiftUI import SwiftData import FirebaseAuth struct ContentView: View { @State private var email = "" @State private var password = "" @State private var alertTitle = "" @State private var showAlert = false @State var showBool = false @EnvironmentObject var gd: GetData @EnvironmentObject var data: AllDrinks var body: some View { NavigationStack{ ZStack{ VStack{ Color(.white) .ignoresSafeArea() Color(.orange) .ignoresSafeArea() } VStack{ Image("logo") .resizable() .scaledToFill() .frame(width: 400, height: 400) Spacer() Group{ TextField("Email", text: $email, prompt: Text("Email")) .textFieldStyle(.roundedBorder) .padding() SecureField("Password", text: $password) .textFieldStyle(.roundedBorder) .padding() } .font(Font.system(size: 25, design: .default)) Spacer() HStack(spacing: 30){ Button{ login() } label: { Text("Login") .foregroundColor(.white) .font(.title2) .fontWeight(.semibold) } .padding() .background(Color.black) .cornerRadius(40) .fullScreenCover(isPresented: $showBool){ AppView() } .alert(alertTitle, isPresented: $showAlert){ Button("OK"){} } message: { Text("") } NavigationLink { SignUpView() } label: { Text("Sign Up") .foregroundColor(.black) .font(.title2) .fontWeight(.semibold) } .padding() .background(Color.white) .cornerRadius(40) } } // vstack } // zstack } // nav .environmentObject(gd) .environmentObject(data) } // body func login(){ Auth.auth().signIn(withEmail: email, password: password){ result, error in guard error == nil else { if let message = error?.localizedDescription { alertTitle = message } showAlert = true return } if let user = Auth.auth().currentUser{ gd.user_name = user.displayName ?? "" gd.fav_path = "user/" + (user.displayName ?? "") + "/favorite" gd.order_path = "user/" + (user.displayName ?? "") + "/order" print("ContentView name:\(gd.user_name)") print("ContentView fav_path:\(gd.fav_path)") print("CintentView order_path:\(gd.order_path)") } alertTitle = "success" showBool = true } } } struct ContentView_Previews: PreviewProvider{ static var previews: some View{ ContentView() .environmentObject(GetData()) .environmentObject(AllDrinks()) } }
Drink.swift
import Foundation import FirebaseFirestore import FirebaseFirestoreSwift import FirebaseAuth class GetData: ObservableObject { @Published var user_name: String = "" @Published var fav_path: String = "" @Published var order_path: String = "" func getName() -> String{ if let user = Auth.auth().currentUser{ user_name = user.displayName ?? "" if user_name != nil{ print("User:\(user_name)") } } return user_name } func setName(name:String){ user_name = name; } func getFavpath() -> String{ var name = getName() fav_path = "user/" + name + "/favorite" return fav_path } func getOrderpath() -> String{ var name = getName() order_path = "user/" + name + "/order" return order_path } } class AllDrinks: ObservableObject{ @Published var allDrinks = [Drink]() @Published var orders = [Drink]() @Published var total = 0 func calTotal(){ total = 0 for i in orders.indices{ total += orders[i].price } } // clear placed orders func removeOrder(){ orders.removeAll() total = 0 } // update all drink data func createDrink(drink: Drink){ let db = Firestore.firestore() do{ try db.collection("drink").document("\(drink.name)").setData(from: drink) } catch { print(error) } } /* // update my favorite drink data func updateFavoriate() { for drink in allDrinks{ createDrink(drink: drink) } } */ // revise favorite data func modifyFavorite(fav: Favorite, id: String) { let db = Firestore.firestore() do{ if let user = Auth.auth().currentUser{ var user_name: String? = user.displayName if user_name != nil{ try db.collection("user").document(user_name ?? "").collection("favorite").document(id).setData(from: fav) print("modifyFavorite Success") } } } catch { print(error) } } } struct Drink: Codable, Identifiable{ var name: String var description: String var Mprice: Int var Lprice: Int var hasHot: Bool = true var isFavorite: Bool = false var type: String @DocumentID var id: String? var size: String = "M" var ice: String = "Normal" var sugar: String = "Normal" var price: Int = 0 } struct Favorite: Codable, Identifiable{ var name: String @DocumentID var id: String? }
修复方案
1. 修正AppView中初始化阶段访问EnvironmentObject的错误
@EnvironmentObject在View的init方法中还未完成注入,此时直接访问会导致崩溃。将用户信息赋值逻辑移到onAppear回调中:
struct AppView: View{ @EnvironmentObject var data: AllDrinks @EnvironmentObject var gd: GetData @State var name: String? init() { UITabBar.appearance().backgroundColor = UIColor.wootea // 移除init中访问gd的代码 } var body: some View{ TabView{ HomeView() .tabItem{ Label("", systemImage: "house") } } .tabViewStyle(DefaultTabViewStyle()) .onAppear { if let user = Auth.auth().currentUser{ gd.user_name = user.displayName ?? "" print("AppView name:\(gd.user_name)") } } } }
2. 为FullScreenCover中的AppView传递EnvironmentObject
在ContentView的登录按钮全屏跳转中,必须传递已有的gd和data实例,否则新打开的AppView会找不到对应对象:
.fullScreenCover(isPresented: $showBool){ AppView() .environmentObject(data) .environmentObject(gd) }
3. 移除冗余的EnvironmentObject注入
AppView的body中无需重复添加.environmentObject(data)和.environmentObject(gd),这些对象会从父视图自动继承,重复添加反而容易造成混淆。
内容的提问来源于stack exchange,提问作者eileen
相关产品推荐
相关产品推荐

