Search code examples
posthttp-postpostmanswiftuihttp-status-codes

SwiftUI reads http response code and can print it in the console but won't run any other code within the if statement


I have created a basic login function using the POST http method. The credentials are sent successfully and the server authentication successfully. The httpResponse.statusCode is sent over and is printed into the console. I am trying to make an Alert httpResponse.statusCode is 400 and load a new view if it is 200. This is the code I am using;

Button(action: {
    let login = self.username
    let passwordstring = self.password
    guard let url = URL(string: "http://localhost:8000/account/auth/") else {return}

    let headers = [
        "Content-Type": "application/x-www-form-urlencoded",
        "cache-control": "no-cache",
        "Postman-Token": "89a81b3d-d5f3-4f82-8b7f-47edc39bb201"
    ]

    let postData = NSMutableData(data: "username=\(login)".data(using: String.Encoding.utf8)!)
    postData.append("&password=\(passwordstring)".data(using: String.Encoding.utf8)!)

    let request = NSMutableURLRequest(url: NSURL(string: "http://localhost:8000/account/auth/")! as URL,
                                      cachePolicy:.useProtocolCachePolicy, timeoutInterval: 10.0)
    request.httpMethod = "POST"
    request.allHTTPHeaderFields = headers
    request.httpBody = postData as Data

    let session = URLSession.shared
    let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) in
        if let httpResponse = response as? HTTPURLResponse {
            guard let data = data else {return}
            print(data)
            if httpResponse.statusCode == 200{
                DispatchQueue.main.async {
                    //Segue to new view goes here
                    print(httpResponse.statusCode)

                    AreaView().animation(.easeIn)
                }
            }else{
                if httpResponse.statusCode == 400{
                    DispatchQueue.main.async {
                        Alert(title: Text("Oops"), message: Text("Username or Password Incorrect"), dismissButton: .default(Text("Got it!")))
                        print(httpResponse.statusCode)

                    }
                }else{
                    DispatchQueue.main.async {
                        Alert(title: Text("Well Damn"), message: Text("Ay chief we have no idea what just happened but it didn't work"), dismissButton: .default(Text("Got it!")))

                    }
                }
            }
            do{

                let JSONFromServer = try JSONSerialization.jsonObject(with: data, options: [])
                let decoder = JSONDecoder()
                decoder.keyDecodingStrategy = .convertFromSnakeCase
                let tokenArray = try decoder.decode(token.self, from: data)
                print(tokenArray.token)
                UserDefaults.standard.set(tokenArray.token, forKey: "savedToken")
                let savedToken = UserDefaults.standard.object(forKey: "savedToken")
                print(savedToken)
            }catch{
                if httpResponse.statusCode == 400{
                    Alert(title: Text("Oops"), message: Text("Username or Password Incorrect"), dismissButton: .default(Text("Got it!")))

                }
                print(error)
                print(httpResponse.statusCode)
            }
        } else if let error = error {

            Alert(title: Text("Well Damn"), message: Text("Ay chief we have no idea what just happened but it didn't work"), dismissButton: .default(Text("Got it!")))

            print(error)
        }
    })
    dataTask.resume()

})//end of login function
{
    Image("StartNow 3").resizable()
        .scaledToFit()
        .padding()
}
    .padding(.horizontal, 15).offset(y: -50)

The AlertView does not popup and the AreaView() doesn't load either. The only command that works within the if statement is print. I've done a bunch of googling to try and find out the issue but SwiftUI is just too new. The console doesn't give me any error messages either. It's worth noting that the code I have was generated by PostMan. Any help is appreciated.


Solution

  • The basic thing you'll need here is to refactor your approach to accommodate SwiftUI's declarative nature, as opposed to UIKit's imperative nature.

    • In UIKit, you have to describe the steps to get your UI to look a certain way (e.g., when this event happens, display this alert).
    • In SwiftUI, you describe what the UI should look like for any app state, and let the framework get you there (e.g. when the app is in this state, an alert is being shown)

    If you look at the warnings you'll get in Xcode, you'll see things like "Result of 'Alert' initializer is unused". This is because you're creating an instance of an Alert, but it is immediately thrown away at the end of the current scope, as it has no connection to your view.

    As a simplified example of what this might look like in SwiftUI:

    struct ContentView: View {
        @State private var showingAreaView = false
        @State private var showingAlert = false
        @State private var alertTitle = ""
        @State private var alertMessage = ""
    
        var body: some View {
            VStack {
                Button("Login") {
                    self.login()
                }
    
                // only show the AreaView when showingAreaView == true
                // this will be set in the completion handler
                if showingAreaView {
                    AreaView()
                        .animation(.easeIn)
                }
            }
            // only present an Alert when showingAlert == true
            // this will be set in the completion handler
            .alert(isPresented: $showingAlert) { () -> Alert in
                Alert(title: Text(alertTitle),
                      message: Text(alertMessage),
                      dismissButton: .default(Text("Got it!")))
            }
        }
    
        func login() {
    
            // setting up request, session, dataTask, etc.
            ...
    
                if httpResponse.statusCode == 200 {
                    // if request is successful, show AreaView
                    DispatchQueue.main.async {     
                        // do something with data here               
                        self.showingAreaView = true
                    }
                } else if httpResponse.statusCode == 400 {
                    // if request is unsuccessful, show Alert
                    DispatchQueue.main.async {
                        self.alertTitle = "Oops"
                        self.alertMessage = "Username or Password Incorrect"
                        self.showingAlert = true
                    }
                }
    
            ...
    
            dataTask.resume()
        }
    }