Swiftui - невозможно получить доступ к объекту среды

avatar
Kwabena Darkwa
9 августа 2021 в 05:26
181
1
0

Я работаю над проектом, в котором я делаю сетевой вызов на сервер и получаю обратно разные данные в зависимости от типа вызова, который я делаю. Проблема, с которой я сталкиваюсь, заключается в доступе к переменным из моих данных, которые декодируются в структуру для отображения в моих представлениях. Сначала структура начинается как 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: { $0.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)
Источник
Ccr
9 августа 2021 в 06:04
0

откуда открывается SearchHistory? можешь поделиться кодом где он открывается?

workingdog support Ukraine
9 августа 2021 в 06:08
0

чтобы в «claimHistroy» (вероятно, с ошибкой) были какие-то данные, вы должны вызвать успешный «signIn (...)», прежде чем использовать его. В вашем ContentView нет вызова "signIn(...)". Где вы вызываете «signIn (...)? Это дома и происходит ли это до того, как вы используете «claimHistroy»?

Kwabena Darkwa
9 августа 2021 в 06:54
0

@workingdog Я называю это при входе в систему. Я отредактировал свой код и добавил его

Kwabena Darkwa
9 августа 2021 в 06:58
0

Представление контента @workingdog не вызывает мое представление входа

Ответы (1)

avatar
workingdog support Ukraine
9 августа 2021 в 06:58
1

в SignIn у вас есть AuthViewModel, а в ContentView у вас есть другая AuthViewModel. Вот почему вы не получаете данные в ClaimHistroy. Это две отдельные модели. Измените структуру кода, чтобы использовать только одну модель AuthViewModel, и передайте ее там, где она вам нужна. Другими словами, используйте:

.
@StateObject var vm: AuthViewModel = AuthViewModel()

перед входом в систему и ContentView.

Kwabena Darkwa
9 августа 2021 в 07:40
0

Омг Спасибо. Я не знаю, почему я не понял этого сначала, потому что моя AuthViewModel - это класс, а не структура.