Swiftui - 无法访问环境对象

Swiftui - unable to access environment object

我正在处理一个项目,我对服务器进行了网络调用,并根据调用类型取回了不同的数据。我面临的问题是从我的数据中访问变量,这些变量被解码成一个结构以显示在我的视图中。该结构一开始为 nil,但在用户登录时解码,然后呈现内容视图。我的 json 能够毫无问题地解码。如果有人对我如何优化我的代码工作有任何建议,将不胜感激。我是 swift 的新手,所以这对我来说都是一次学习经历

这是我的一些网络代码

struct NetworkService {
   
    static let shared = NetworkService()
    
    // Singleton that Only allows one instance of this class 
    private init() {}
    
    func request<T: Decodable>(endPoint: EndPoint, method: Method, parameters: [String: Any]? = nil, completion: @escaping(Result<T, Error>) -> Void) {
        // Creates a urlRequest
        guard let request = createRequest(endPoint: endPoint, method: method, parameters: parameters) else {
            completion(.failure(AppError.invalidUrl))
            return
        }
        
        let session = URLSession.shared
        
        session.dataTask(with: request) { data, response, error in
            var results: Result<Data, Error>?
            
            guard let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 else {
                completion(.failure(AppError.badStatusCode))
                return
            }
            
            if let response = response {
                /*
                // Gets the JSESSIONID - Remove?
                let cookieName = "JSESSIONID"
                if let cookie = HTTPCookieStorage.shared.cookies?.first(where: { [=10=].name == cookieName })  {
                    debugPrint("\(cookieName): \(cookie.value)")
                }*/
               
                print(response)
            }
            
            if let data = data {
                results = .success(data)
                
                
            } else if let error = error {
                results = .failure(error)
                print("Server Error: \(error.localizedDescription)")
            }
            
            DispatchQueue.main.async {
                self.handleResponse(result: results, completion: completion)
            }
            
        }.resume()
    }
    
    
    /// Helper function that decodes JSON data response from server
    private func handleResponse<T: Decodable>(result: Result<Data, Error>?, completion: (Result<T, Error>) -> Void) {
        guard let result = result else {
            completion(.failure(AppError.unknownError))
            return
        }
        
        switch result {
        
            case .success(let data):
                
                do {
                    let json = try JSONSerialization.jsonObject(with: data, options: [])
                    print("Server JsonObject response: \(json)")
                } catch {
                    completion(.failure(AppError.errorDecoding))
                }
                
                let decoder = JSONDecoder()
                // Decodes that json data
                do {
                    let json = try decoder.decode(T.self, from: data)
                    completion(.success(json))
                    
                } catch {
                    print("This caused the problem")
                    completion(.failure(error))
                }
                
                
            case .failure(let error):
                completion(.failure(error))
        }
        
    }

这是我的 AuthViewModel,它是解码我的 json 数据的真实来源

class AuthViewModel: ObservableObject {
    
    @Published var user: LoginResponseData.Root? = nil
    @Published var alert: CustomAlert? = nil
    @Published var claimHistroy: ClaimStruct? = nil
    
    var authentication: AuthenticationCheck? = nil
    
    var showLoader = false
    
    let networkService: NetworkService = NetworkService.shared
    
    func signIn(username: String, password: String) {
        networkService.signIn(username: username, password: password) { (result) in
            switch result {
            case .success(let user):
                print("This user last name is: \(user.result.login.userName.name.fullName)")
                self.user = user
                 
                self.authentication?.updateValidation(success: true)
                
                // Calls method
                self.profileSessionMember()
                
            case .failure(let error):
                print("The error is: \(error.localizedDescription)")
                //Reset the variable
                //self.user = nil
                self.authentication?.updateValidation(success: false)
                // Pass a message to the user 
                self.alert = CustomAlert(title: "Invalid Credentials", message: "Either username or password is incorrect. Please try again")
                
            }
        }
    }
    
    /// Gets the Search Savings Amouny from decoded struct
    func profileSessionMember() {
        networkService.profileSessionMember { result in
            switch result {
            case .success(let userHistory):
                // Sets the claimHistory
                self.claimHistroy = userHistory
                print("This happened")
                print(self.claimHistroy?.result.member.yearToDateSearchSavingsAmount ?? "didnt work")
            case .failure(let error):
                print(error)
            
            }
        }
    }
    
    
    
    
    func logout() {
        self.user = nil
        authentication?.updateValidation(success: false)
    }
    
    
    //This assists in creating a shared alert
    struct CustomAlert : Identifiable {
        let id: UUID = UUID()
        var title: String
        var message: String
    }
    
    
    
} // End of AuthViewModel


class AuthenticationCheck: ObservableObject {
    @Published var isValidated = false
    
    func updateValidation(success: Bool) {
        withAnimation {
            isValidated = success
        }
    }
}

这是我将对象传递给环境的一些内容视图

struct ContentView: View {
    @StateObject var test: AuthViewModel = AuthViewModel()
    
    var body: some View {
        
        TabView {
           Home()
                .tabItem {
                    Image(systemName: "house.fill")
                    Text("Home")
                }
         
            Search()
                .font(.system(size: 30, weight: .bold, design: .rounded))
                .tabItem {
                    Image(systemName: "magnifyingglass")
                    Text("Search")
                }
            
            Text("Profile Page")
                .font(.system(size: 30, weight: .bold, design: .rounded))
                .tabItem {
                    Image(systemName: "person.fill")
                    Text("Profile")
                }
        }//tab view ends here
        .environmentObject(test)

这段代码不起作用,但仍然显示为 nill,在下面有评论

struct SearchHistory: View {
    @EnvironmentObject var vm: AuthViewModel
    
    var body: some View {
        VStack {
            Text("BASED ON YOUR SEARCH HISTORY")
                .font(.title3)
                .fontWeight(.heavy)
                .foregroundColor(Color(red: 0.584, green: 0.655, blue: 0.992, opacity: 100.0))
                .multilineTextAlignment(.center)
                .padding()
            
            HStack {
                Image(systemName: "dollarsign.circle")
                    .resizable()
                    .frame(width: 43.0, height: 44.0)
                    .aspectRatio(contentMode: .fill)
                    .padding()
                
                VStack{
                    Text("Total")
                        .multilineTextAlignment(.center)
                    Text("Amount")
                        .multilineTextAlignment(.center)
                    // Display Search Saving
                    // This shows up as nil
                    Text(String(describing: vm.claimHistroy?.result.member.yearToDateSearchSavingsAmount))

这是我登录的地方

struct SignIn: View {
    @StateObject var vm: AuthViewModel = AuthViewModel()
    @EnvironmentObject var authentication: AuthenticationCheck
    @Binding var userID: String
    @Binding var passcode: String
    
    var body: some View {
        Button(action: {
            // Remove
            print("Button action")
            vm.signIn(username: userID, password: passcode)
            
        }) {
            Text("Sign In")
                .multilineTextAlignment(.center)
                .padding()
        }
        .frame(width: 150.0, height: 43.0)

在 SignIn 中您有一个 AuthViewModel,在 ContentView 中您有另一个 AuthViewModel。这就是为什么您没有在 claimHistroy 中获取数据的原因。这是两个独立的模型。重组您的代码以仅使用一个 AuthViewModel,并将其传递到您需要的地方。换句话说,使用:

@StateObject var vm: AuthViewModel = AuthViewModel()

在登录和 ContentView 之前。