Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
2.1k views
in Technique[技术] by (71.8m points)

json - perform segue swift 4

I am trying to validate the login of a user with my database. When the login button is pressed I want to say whether a segue will happen or no based on the info returned back from the database. I start with a variable decision set to true initially and if the I can't validate the user I want to set it to false and prevent segue. This is the code that I have, but there is a problem with it. The return statement at the end is always true. What happens basically is the return statement at the end after .resume() get called first before the response is returned from the database. Can someone clarify why this is happening

 override func shouldPerformSegue(withIdentifier identifier: String,sender: 
 Any?) -> Bool
 {
             var decision = true
            let url = URL(string:"http://192.23.25.98/login/php")
            var request = URLRequest(url: url!)
            request.httpMethod = "POST"
            let body = "UserNameLogIn=(userName.text!.lowercased())&PasswordLogIn=(passWord.text!.lowercased())"
            request.httpBody=body.data(using: String.Encoding.utf8)
            URLSession.shared.dataTask(with: request) { (data:Data?, response:URLResponse?, error:Error?) in
                if (error == nil)
               {
                    DispatchQueue.main.async(execute: {
                        do
                        {
                            let json = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as? Dictionary<AnyHashable,AnyObject>
                            guard let parseJson = json else{
                                print ("error parsing")
                                return
                            }
                            let status = parseJson["status"]
                            if (status != nil)
                            {
                                if (parseJson.count>3)
                                {
                                    decision = true

                                }
                                else
                                {
                                    decision = false


                                }
                            }
                        }
                        catch
                        {

                            print("error: (error)")
                        }
                    })
                }
                else
                {
                    decision = false
                }
                }.resume()
    return decision
}
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Rather than initiating the segue on the tap of the button and trying to capture an asynchronous process within shouldPerformSegue(withIdentifier:sender:), you instead should just have the button define a @IBAction that performs the validation request and programmatically initiates the segue if it succeeded.

Thus:

  1. Remove the segue from the button.

  2. Add segue between the view controllers by control-dragging from the view controller icon in the bar above the first scene to the second scene:

    enter image description here

  3. Select the segue and give it a name in the attributes inspector:

    enter image description here

  4. Having removed the segue from the button, you can now hook up your button to an @IBAction method:

    enter image description here

  5. Your login action should performSegue(withIdentifier:sender:) when you finish your validation logic:

    performSegue(withIdentifier: "NextSceneSegue", sender: self)
    

    E.g.

    @IBAction func didTapLoginButton(_ sender: Any) {
        let url = URL(string:"http://192.23.25.98/login/php")
        var request = URLRequest(url: url!)
        request.httpMethod = "POST"
        request.httpBody = ["UserNameLogIn": useridTextField.text!, "PasswordLogIn": passwordTextField.text!]
            .map { $0.key + "=" + $0.value.addingPercentEncoding(withAllowedCharacters: .urlQueryValueAllowed)! }
            .joined(separator: "&")
            .data(using: .utf8)
    
        URLSession.shared.dataTask(with: request) { data, response, error in
            DispatchQueue.main.async {
                guard let data = data, let httpResponse = response as? HTTPURLResponse, (200 ..< 300) ~= httpResponse.statusCode, error == nil else {
                    // handle basic network errors here
                    return
                }
    
                guard let json = (try? JSONSerialization.jsonObject(with: data)) as? [String: Any] else {
                    // handle json parsing errors here
                    return
                }
    
                if json["status"] == nil  {
                    // handle missing status here
                    return
                }
    
                guard json.count > 3 else {
                    // handle incorrect count here
                    return
                }
    
                self.performSegue(withIdentifier: "NextSceneSegue", sender: self)
            }
        }.resume()
    }
    
  6. Note, I percent encode the values in the body of the request. Passwords, in particular, might contain reserved characters. And I don't make the mistake of using .urlQueryAllowed, but instead used .urlQueryValueAllowed, in which I pulled out a few reserved characters:

    extension CharacterSet {
    
        /// Returns the character set for characters allowed in the individual parameters within a query URL component.
        ///
        /// The query component of a URL is the component immediately following a question mark (?).
        /// For example, in the URL `http://www.example.com/index.php?key1=value1#jumpLink`, the query
        /// component is `key1=value1`. The individual parameters of that query would be the key `key1`
        /// and its associated value `value1`.
        ///
        /// According to RFC 3986, the set of unreserved characters includes
        ///
        /// `ALPHA / DIGIT / "-" / "." / "_" / "~"`
        ///
        /// In section 3.4 of the RFC, it further recommends adding `/` and `?` to the list of unescaped characters
        /// for the sake of compatibility with some erroneous implementations, so this routine also allows those
        /// to pass unescaped.
    
        static var urlQueryValueAllowed: CharacterSet = {
            let generalDelimitersToEncode = ":#[]@"    // does not include "?" or "/" due to RFC 3986 - Section 3.4
            let subDelimitersToEncode = "!$&'()*+,;="
    
            var allowed = CharacterSet.urlQueryAllowed
            allowed.remove(charactersIn: generalDelimitersToEncode + subDelimitersToEncode)
            return allowed
        }()
    
    }
    

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...