Return Bool in Alamofire closure

2020-04-07 05:25发布

问题:

I use Swift 2 and Xcode 7.1.

I have a function who connect my users, but it will connect at my database with HTTP. I use Alamofire for execute this request. I want to know, from a view controller if the user is connected.

I have my function connect in a class. And i test connection in a ViewController. Like this :

    class user {

    // ...

    func connectUser(username: String, password: String){

        let urlHost = "http://localhost:8888/project350705/web/app_dev.php/API/connect/"
        let parametersSymfonyG = [
            username, password
        ]
        let url = UrlConstruct(urlHost: urlHost).setSymfonyParam(parametersSymfonyG).getUrl()

        //var userArray = [String:AnyObject]()

        Alamofire.request(.GET, url)
            .responseString { response in

                if let JSON = response.result.value {

                    var result = self.convertStringToDictionary(JSON)!

                    if result["status"] as! String == "success"{
                        let userArray = result["user"] as! [String:AnyObject]
                        userConnect = self.saveUser(userArray)
                    } else{
                        print("ERROR-CONNECTION :\n Status :\(result["status"]!)\n Code :\(result["code"]!)")
                    }
                    return ""
                }
        }
    }

    // ...
}

class MyViewController: UIViewController {

    // ...

    @IBAction func connect(sender: AnyObject?) {

        // CONNECTION
        User.connectUser(self.username.text!, password: self.password.text!)

        // CHECK
        if userConnect != nil {
            print("connected")
        }else{
            print("NotConnected")
        }
    }

    // ...

}

First solution : Return

To do so would require that my function returns a Boolean. Only I can not use return.

Alamofire.request(.GET, url)
        .responseString { response in

            if let JSON = response.result.value {

                var result = self.convertStringToDictionary(JSON)!

                if result["status"] as! String == "success"{
                    let userArray = result["user"] as! [String:AnyObject]
                    userConnect = self.saveUser(userArray)
                } else{
                    print("ERROR-CONNECTION :\n Status :\(result["status"]!)\n Code :\(result["code"]!)")
                }
                return "" // Unexpected non-void return value in void function
            }
    }

Second solution :

I can also test if the user has been logged, but before testing, I must wait for the function have finished loading.

users.connectUser(self.username.text!, password: self.password.text!)

// after 
if userConnect != nil {
    print("connected")
}else{
    print("NotConnected")
}

I would prefer return a boolean. It will facilitate the processing. Do you have a solution ?

回答1:

I would suggest employing a completion handler in your connectUser method:

func connectUser(username: String, password: String, completion: @escaping (Bool) -> Void) {
    // build the URL

    // now perform request

    Alamofire.request(url)
        .responseString { response in
            if let json = response.result.value, let result = self.convertStringToDictionary(json) {
                completion(result["status"] as? String == "success")
            } else {
                completion(false)
            }
    }
}

You can then call it using:

users.connectUser(username.text!, password: password.text!) { success in
    if success {
        print("successful")
    } else {
        print("not successful")
    }
}
// But don't use `success` here yet, because the above runs asynchronously

BTW, if your server is really generating JSON, you might use responseJSON rather than responseString, further streamlining the code and eliminating the need for convertStringToDictionary:

func connectUser(username: String, password: String, completion: @escaping (Bool) -> Void) {
    // build the URL

    // now perform request

    Alamofire.request(url)
        .responseJSON { response in
            if let dictionary = response.result.value as? [String: Any], let status = dictionary["status"] as? String {
                completion(status == "success")
            } else {
                completion(false)
            }
    }
}

If you've written your own server code to authenticate the user, just make sure you set the right header (because responseJSON not only does the JSON parsing for you, but as part of its validation process, it makes sure that the header specifies JSON body; it's good practice to set the header, regardless). For example in PHP, before you echo the JSON, set the header like so:

header("Content-Type: application/json");


回答2:

The completion handler of your Alamofire.request method is asynchronous and it doesn't have a return type specified in its signature. Thats why you see an error when you provide a return statement in your completion handler closure.

You will have to split your request and response processing to separate methods and call the response processing method instead of using return statement.

Alamofire.request(.GET, url).responseString { response in

        if let JSON = response.result.value {
            var result = self.convertStringToDictionary(JSON)!

            if result["status"] as! String == "success"{
                let userArray = result["user"] as! [String:AnyObject]
                userConnect = self.saveUser(userArray)
                processSuccessResponse() //Pass any parameter if needed
            } else{
                print("ERROR-CONNECTION :\n Status :\(result["status"]!)\n Code :\(result["code"]!)")
                processFailureResponse() //Pass any parameter if needed
            }
       }
}

func processSuccessResponse() {
    //Process code for success
}

func processFailureResponse() {
    //Process code for failure
}


回答3:

My preferred way of doing this is to call a function in the completion handler. You can also set a boolean flag in order to check if the user is connected at any given time.

func connectUser(username: String, password: String, ref: MyClass) {
    Alamofire.request(.GET, url)
        .responseString { response in

            var userIsConnected = false

            if let JSON = response.result.value {

                var result = self.convertStringToDictionary(JSON)!

                if result["status"] as! String == "success"{
                    let userArray = result["user"] as! [String:AnyObject]
                    userConnect = self.saveUser(userArray)
                    userIsConnected = true
                } else {
                    print("ERROR-CONNECTION :\n Status :\(result["status"]!)\n Code :\(result["code"]!)")
                }

            } else {
                print("Response result nil")
            }

            ref.finishedConnecting(userIsConnected)
        }
    }
}

class MyClass {
    var userIsConnected = false

    func startConnecting() {
        connectUser(username, password: password, ref: self)
    }

    func finishedConnecting(success: Bool) {
        userIsConnected = success

        ... post-connection code here
    }
}