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
}