Swift – Pass json data to other views

Solution for Swift – Pass json data to other views
is Given Below:

Im trying to figure out how I could pass my json decoded data that I receive from a successful Result so I can start using it to display specific info on another screen view. Iv been trying to figure it out with no luck, Im new to swift as well as app development so this is all a learning experience for me. If anyone can help that would be much appreciated. This is some of my network code

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
                let cookieName = "JSESSIONID"
                if let cookie = HTTPCookieStorage.shared.cookies?.first(where: { $0.name == cookieName })  {
                    debugPrint("(cookieName): (cookie.value)")
                }
               
                print(response)
            }
            
            // Look into this
            if let data = data {
                results = .success(data)
                
                /*
                // Converts data to readable String
                let responseString = String(data: data, encoding: .utf8) ?? "unable to convert to readable String"
                print("Server Response: (responseString.description)")
                */
                
            } else if let error = error {
                print("NO this happen")
                results = .failure(error)
                print("Server Error: (error.localizedDescription)")
            }
            
            DispatchQueue.main.sync {
                self.handleResponse(result: results, completion: completion)
            }
            
        }.resume()
    }
    
   
    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 {
                    completion(.failure(error))
                }
                
                
            case .failure(let error):
                print("This happen")
                completion(.failure(error))
        }
        
    }

This is the function I use to create my request

 func signIn(username: String, password: Any, completion: @escaping(Result<LoginResponseData.Root, Error>) -> Void) {
        let params = ["username": "(username)", "password": "(password)"]
        
        request(endPoint: .Login, method: .post, parameters: params, completion: completion)
    }

This is my login view code

struct SignIn: View {
    @Binding var userID: String
    @Binding var passcode: String
    @State private var showAlert = false
    @EnvironmentObject var authentication: AuthenticationCheck
    
    var body: some View {
        Button(action: {
            // Remove
            print("Button action")
            
            NetworkService.shared.signIn(username: userID, password: passcode) { (result) in
                switch result {
                case .success(let user):
                    print("This user last name is: (user.result.login.userName.name.fullName)")
                    
                    authentication.updateValidation(success: true)
                    showAlert = false
                    
                case .failure(let error):
                    print("The error is: (error.localizedDescription)")
                    showAlert.toggle()

                }
            }
            
            
        }) {
            Text("Sign In")
                .multilineTextAlignment(.center)
                .padding()
        }
        .frame(width: 150.0, height: 43.0)
        .background(/*@[email protected]*//*@[email protected]*/Color(red: 0.584, green: 0.655, blue: 0.992)/*@[email protected]*/)
        .foregroundColor(.white)
        .cornerRadius(20)
        .disabled(userID.isEmpty || passcode.isEmpty)
        .alert(isPresented: $showAlert, content: {
            Alert(title: Text("Invalid Credentials"), message: Text("Either username or password is incorrect. Please try again"), dismissButton: .cancel())
        })
        
    }

This is my authentification check & App

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

@main
struct SomeApp: App {
    @StateObject var authenticationCheck = AuthenticationCheck()
    
    var body: some Scene {
        WindowGroup {
            if authenticationCheck.isValidated {
                ContentView()
                    .environmentObject(authenticationCheck)
            } else {
                Signin()
                    .environmentObject(authenticationCheck)
            }
            //TestView()
        }
    }
}

Here is a simplified version of your code

class AuthViewModel: ObservableObject {
    // I don't have your Model info
    //You can have an empty object of your model or you can make it an optional, etc
    @Published var user: YourModel? = nil
    @Published var alert: CustomAlert? = nil
    let networkService: NetworkService = NetworkService.shared
    var authentication: AuthenticationCheck? = nil
    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)")
                //Assign you value to the Published variable here
                self.user = user
                self.authentication?.updateValidation(success: true)
                self.alert = CustomAlert(title: "Success", message: "You logged in")
                
            case .failure(let error):
                print("The error is: (error)")
                //Reset the variable
                self.user = nil
                self.authentication?.updateValidation(success: false)
                //You can pass a better message to the user like this
                self.alert = CustomAlert(title: "Invalid Credentials", message: "(error)")

            }
        }
    }
    
    func logout() {
        self.user = nil
        authentication?.updateValidation(success: false)
        self.alert = CustomAlert(title: "See you later", message: "You logged out")
    }
}
class AuthenticationCheck: ObservableObject {
    @Published var isValidated = false
    func updateValidation(success: Bool) {
        withAnimation {
            isValidated = success
        }
    }
}
struct SampleNetworkView: View {
    @StateObject var vm: AuthViewModel = AuthViewModel()
    @StateObject var authentication: AuthenticationCheck = AuthenticationCheck()
    @State var username: String = ""
    @State var password: String = ""
    var body: some View {
        NavigationView{
            switch authentication.isValidated{
            case true:
                VStack{
                Text("Signed In - you are now in the content view")
                    .toolbar(content: {
                        Button("log out", action: {
                            vm.logout()
                        })
                    })
                }
            case false:
                VStack{
                    TextField("username", text: $username).textFieldStyle(RoundedBorderTextFieldStyle())
                    SecureField("password", text: $password).textFieldStyle(RoundedBorderTextFieldStyle())
                    Button("sign in", action: {
                        vm.signIn(username: username, password: password)
                    }).disabled(username.isEmpty || password.isEmpty)
                }
            }
        
        }
        //Inject the StateObjects to the navigation view so you can access the variables
        .environmentObject(authentication)
        .environmentObject(vm)
        //Create a shared Alert for the ViewModel
        .alert(item: $vm.alert, content: { customAlert in
            Alert(title: Text(customAlert.title), message: Text(customAlert.message), dismissButton: .default(Text("ok")))
        })
        //Pass the authentication to the ViewModel so you can pass info
        .onAppear(perform: {
            vm.authentication = authentication
        })
    }
}

struct SampleNetworkView_Previews: PreviewProvider {
    static var previews: some View {
        SampleNetworkView()
    }
}
//This assists in creating a shared alert
struct CustomAlert : Identifiable {
    let id: UUID = UUID()
    var title: String
    var message: String
}