iosswift

How to wait a value from api response for login view using swift


I started learning Swift and are not familiar with synchronous and asynchronous operations with swift code. I want to be in my login view (viewcontroller), when the user enters the ID, PASSWORD, and then requests the service from the API, the API will compare the database data is correct, if the correct return json data => true, the error returns json data => false

I don't know how to make my login() execute after getting the API response.

I have researched a lot of information about this, including NSOperationQueue ... etc.

But the final result of the implementation failed, please help me with experienced people.

var jsonmessage: Bool? = nil

when onclickLogin will go next page using segue

 @IBAction func onclickLogin(_ sender: Any) {
        
            Postusercheck()
            login()
}

request api

 func Postusercheck(){
        let parameters = ["ID":txtcount.text,"Password":txtpaswoerd.text] //post request with id,password
        print(parameters)
        let url = URL(string: "http://" + apiip + "/api/user")! //change the url
        let session = URLSession.shared
        //now create the URLRequest object using the url object
        var request = URLRequest(url: url)
        request.httpMethod = "POST" //set http method as POST
        do {
            request.httpBody = try JSONSerialization.data(withJSONObject: parameters, options: .prettyPrinted) // pass
        } catch let error {
            print(error.localizedDescription)
        }
        request.addValue("application/json", forHTTPHeaderField: "Content-Type")
        request.addValue("application/json", forHTTPHeaderField: "Accept")
        let task = session.dataTask(with: request as URLRequest, completionHandler: { data, response, error in
            guard error == nil else {
                return
            }
            guard let data = data else {
                return
            }
            do {
                if let json = try JSONSerialization.jsonObject(with: data, options: .mutableContainers) as? [String: Any] {
                    print(json)
                    self.jsonmessage = json["message"] as? Bool
                    print("Title: \(String(describing:  self.abc)))")
                    }
                } catch let error {
                print(error.localizedDescription)
                                  }
        })
                task.resume()
    }
 func login(){
        
    if (self.jsonmessage == true){
    }
    else if txtcount.text == "" || txtpaswoerd.text == "" {
    let alert = UIAlertController(title: "Don't empty the field", message: "Please enter again", preferredStyle: .alert)
    let ok = UIAlertAction(title: "OK", style: .default, handler: nil)
    
    alert.addAction(ok)
    present(alert, animated: true, completion: nil)
    
        }
        else
            {
                let alert = UIAlertController(title: "ID OR PASSWORD ERROR", message: "Please enter again", preferredStyle: .alert)
                let ok = UIAlertAction(title: "OK", style: .default, handler: nil)
                alert.addAction(ok)
                present(alert, animated: true, completion: nil)
            }
    }

========================updated========code======

@IBAction func onclickLogin(_ sender: Any) {
       
        Postusercheck()
}
 
    func Postusercheck(){
        
        let parameters = ["ID":txtcount.text,"Password":txtpaswoerd.text] //post request with id,password
        print(parameters)
        let url = URL(string: "http://" + apiip + "/api/user")! //change the url
        let session = URLSession.shared
        //now create the URLRequest object using the url object
        var request = URLRequest(url: url)
        request.httpMethod = "POST" //set http method as POST
        do {
            request.httpBody = try JSONSerialization.data(withJSONObject: parameters, options: .prettyPrinted) // pass
        } catch let error {
            print(error.localizedDescription)
        }
        request.addValue("application/json", forHTTPHeaderField: "Content-Type")
        request.addValue("application/json", forHTTPHeaderField: "Accept")
        let task = session.dataTask(with: request as URLRequest, completionHandler: { data, response, error in
            guard error == nil else {
                return
            }
            guard let data = data else {
                return
            }
            do {
                if let json = try JSONSerialization.jsonObject(with: data, options: .mutableContainers) as? [String: Any] {
                    print(json)
                    self.jsonmessage = json["message"] as? Bool
                    self.login()
                    
                    }
                } catch let error {
                print(error.localizedDescription)
                                  }
        })
                task.resume()
    }
func login(){
    if (self.jsonmessage == true){
        //Navigate to the another view controller
        let mainStoryboard = UIStoryboard.init(name: "Main", bundle: nil)
        let anotherViewController = mainStoryboard.instantiateViewController(withIdentifier: "AnotherViewController")
        self.navigationController?.pushViewController(anotherViewController, animated: true)
    }
    else if txtcount.text == "" || txtpaswoerd.text == "" {
        let alert = UIAlertController(title: "Don't empty the field", message: "Please enter again", preferredStyle: .alert)
        let ok = UIAlertAction(title: "OK", style: .default, handler: nil)
        
        alert.addAction(ok)
        present(alert, animated: true, completion: nil)
    }
    else
    {
        let alert = UIAlertController(title: "ID OR PASSWORD ERROR", message: "Please enter again", preferredStyle: .alert)
        let ok = UIAlertAction(title: "OK", style: .default, handler: nil)
        alert.addAction(ok)
        present(alert, animated: true, completion: nil)
    }
}

When I try the new code.

I have canceled the original segue connection. I set the Storboard ID to AnotherViewController on the page I want, but when the account password is successfully verified, he does not go to the next page.

He just stopped at the original page (after the account password verification is successful) If I enter the wrong account password, it still get an error message. This function is useful.


Solution

  • Make the function call in the completion handler of URLSession like below :

        let task = session.dataTask(with: request as URLRequest, completionHandler: { data, response, error in
                    guard error == nil else {
                        return
                    }
                    guard let data = data else {
                        return
                    }
                    do {
                        if let json = try JSONSerialization.jsonObject(with: data, options: .mutableContainers) as? [String: Any] {
                            print(json)
                            self.jsonmessage = json["message"] as? Bool
                            self.login()
                            print("Title: \(String(describing:  self.abc)))")
                            }
                        } catch let error {
                        print(error.localizedDescription)
                                          }
                })
    

    You have to remove the login call from the button's IBAction cause when you are calling the Postusercheck it is going to make the webservice call and it will not wait for the response as the session.dataTask is asynchronous. So the execution will go back to the IBAction and login() will be called though you have not received the web service response and you don't know if the user name and passwords are correct. So you have to remove login call from the button click action like this :

    @IBAction func onclickLogin(_ sender: Any) {
    
                Postusercheck()
    }
    

    Change the login function as below :

    func login(){
    
        if (self.jsonmessage == true){
           //Navigate to the another view controller
           let mainStoryboard = UIStoryboard.init(name: "Main", bundle: nil)
           let anotherViewController = mainStoryboard.instantiateViewController(withIdentifier: "AnotherViewController")
           self.navigationController?.pushViewController(anotherViewController, animated: true)    
        }
        else if txtcount.text == "" || txtpaswoerd.text == "" {
        let alert = UIAlertController(title: "Don't empty the field", message: "Please enter again", preferredStyle: .alert)
        let ok = UIAlertAction(title: "OK", style: .default, handler: nil)
    
        alert.addAction(ok)
        present(alert, animated: true, completion: nil)
    
            }
            else
                {
                    let alert = UIAlertController(title: "ID OR PASSWORD ERROR", message: "Please enter again", preferredStyle: .alert)
                    let ok = UIAlertAction(title: "OK", style: .default, handler: nil)
                    alert.addAction(ok)
                    present(alert, animated: true, completion: nil)
                }
        }
    

    Here make sure that you are having a view controller in the storyboard named "Main" with Storyboard Id as "AnotherViewController" otherwise it won't work.

    Another option for navigation is through segues in the storyboards. Below is a great tutorial to learn great things about the storyboard.

    https://www.raywenderlich.com/464-storyboards-tutorial-for-ios-part-1

    Also one more thing is that Postusercheck() is not so good function name. Please refer below guide line for the best practices in the swift's widely used naming conventions :

    https://github.com/raywenderlich/swift-style-guide