简体   繁体   中英

Changing views via conditional statements inside of async commands in SwiftUI IOS15

I'm trying to have a user go onto the ContentView and login, now this will either log them in or return to the ContentView (Will create errors and things later for failed login). At the moment I can login and I pull the required data from my API, it will then run a conditional of true if they're successful or false if they are not. Inside the conditional is a print statement of Success if true or Unsuccessful if false. This all runs fine but when I put a statement in to change views it doesn't change the view. I'm sure its got to do with async/await but I'm pretty new to SwiftUI. I have included the ContentView code for reference. Its not a super complicated process but I can't seem to find much information about changing view in this manner. It might be the wrong approach, so any advice would be awesome.

import SwiftUI

struct ContentView: View {
    @State var username : String = ""
    @State var password : String = ""
    @State var loggedIn : Bool = false
    @State var network = NetworkCall()
    var body: some View {
        ZStack {
            Color(red: 0.4627, green: 0.8392, blue: 1.0).ignoresSafeArea()
            VStack {
                Text("Login")
                    .padding()
                TextField("Username", text: $username)
                    .padding()
                SecureField("Password", text: $password)
                    .padding()
                
                Button(action: {
                    Task {
                        do{
                            try await loggedIn = network.LoginFunction(userName: username, passWord: password)
                            if(loggedIn){
                                print("Login Successful")
                                Dashboard()
                            }
                            else {
                                print("Login Unsuccessful")
                                ContentView()
                            }
                        }catch{
                            print("error", error)
                        }                        
                    }
                }) {
                    Text("Sign in")
                        .padding()
                        .frame(minWidth: 100, maxWidth: 300, minHeight: 44)
                        .foregroundColor(Color.white)
                        .cornerRadius(30)
                        .background(Color.blue)
                        .cornerRadius(20)
                }
            }
        }
    }
}

The Network Caller

import Foundation
class NetworkCall {
    func LoginFunction(userName: String, passWord: String) async throws -> Bool {
        
        let parameters = "{\r\n    \"uEmail\" : \"" + userName + " \",\r\n    \"uPassword\" : \"" + passWord + "\" \r\n}"
        let postData = parameters.data(using: .utf8)
        
        var request = URLRequest(url: URL(string: "https://..........................")!,timeoutInterval: Double.infinity)
        
        request.addValue("application/json", forHTTPHeaderField: "Content-Type")
        request.httpMethod = "POST"
        request.httpBody = postData

        let (data, response) = try await URLSession.shared.data(for: request)
        guard (response as? HTTPURLResponse)?.statusCode == 200 else { fatalError("Error while fetching data") }
        let decodedUser = try JSONDecoder().decode(UserClass.self, from: data)
        print(decodedUser.userinfo.user_guid)
        
        if(decodedUser.status){
            return true
        }
        else{
            return false
        }
    }

}


SwiftUI renders your user interface in response to changes in state. So instead of trying to render Dashboard or ContentView within your Task , instead you need to set some form of state higher up that determines which view to show to the user.

For example, if you had a @State variable recording your logged in status, your code would start to look like

struct ContentView: View {
  @State var loggedIn = false

  var body: some View {
    if loggedIn {
      Dashboard()
    } else {
      // login form
      Button {
        Task {
          let logInStatus = try await network.LoginFunction(userName: username, passWord: password)
          self.loggedIn = logInStatus
        }
      }
    }
  }
}

When your async tasks changes the ContentView's state, SwiftUI's rendering subsystem will pick up that loggedIn has changed, and re-render body so that the login form is replaced by a call to Dashboard() .

Another approach that you may want to consider is creating two Views:

  • LoginView
  • DashBoardView

Both is placed in ContentView and you have state loggedIn in ContentView. And you display which View depend on this loggedIn state.

You can pass this loggenIn state to LoginView so that it can modify it based on the log in result from the API.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM