0

Here is what I am trying to do:

var usernameCheckerResponse : String = ""

 //This IBAction is a UITextfield that sends post request when editing is finshed.
 @IBAction func usernameChecker(_ sender: Any) {

 // perform post request with URLSession
 // post request returns url response from URLSession 
 // the value of this response is either 'usernameExists' or 'usernameAvailable'
 // usernameCheckerResponse = String(describing : response) 

}
//use modified usernameCheckerResponse variable outside the IBAction function. 
//For example like this:

   func UsernameExists () -> Bool {
   if(usernameCheckerResponse == "usernameExists"){
   return true
  } else { return false }
}

I am aware that an IBAction will only return a void, so is there anyway around this problem? Any help and/or advice will be greatly appreciated.

BazDaz
  • 27
  • 6

2 Answers2

1

Yes absolutely. Here is an example,

var usernameCheckerResponse : String = ""

 //This IBAction is a UITextfield that sends post request when editing is finshed.
 @IBAction func usernameChecker(_ sender: Any) {

 //post request
 // post request returns url response
 // usernameCheckerResponse = String(describing : response) 

}
//use modified usernameCheckerResponse variable outside the IBAction function.

func accessVariable() {
   print("\(usernameCheckerResponse")
}

Keep in mind that the trick here is to access the variable when it has changed. To do that you need to pick some sort of way to keep track of that. Delegation is probably the most standard way to do that. See this. You would have to be more specific as to why you want the variable changed, because I would need to know what is using it (delegation required that you have are very specific on who is participating).

I would like to also be more specific with how delegation works. You would specify when the 'accessVariable()' function is called, in the place where you want the modified variable (this would always be between two different classes or structures). Keep in mind that you do not need to use delegation if you are just trying to share the variable in the same class. Calling the function 'accessVariable()' will suffice. However if this is the case where you want something to happen in the same class, but you really want to control in what order the functions finish then you need to use callbacks.

BTW Leo, doing it that way will make the app crash...

iOSGuy93892
  • 137
  • 7
  • Firstly, thank you both for your responses, once I have modified and retrieved the variable, I want to use it in another func inside the same class. I want to use it in an IF statement, to see if the string matches a certain value. The func will return a boolean depending on the result. – BazDaz Nov 06 '17 at 23:43
0

In general, you should think of IBAction functions as connection points for controls like buttons etc. You would never call it yourself. If you need to do that, make another function and have the IBAction function call that.

Since you are using URLSession to fetch the data from an external source, you will need to be aware that this does not happen synchronously. Send the call to your API and have the completion handler get called when data is returned.

All of this code goes into your ViewController

// Set up a reusable session with appropriate timeouts
internal static var session: URLSession  {
    let sessionConfig = URLSessionConfiguration.default
    sessionConfig.timeoutIntervalForRequest = 6.0
    sessionConfig.timeoutIntervalForResource = 18.0

    return URLSession( configuration: sessionConfig )
}


// Create an httpPost function with a completion handler
// Completion handler takes :
//   success: Bool true/false if things worked or did not work
//   value: String string value returned or "" for failures
//   error: Error? the error object if there was one else nil
func httpPost(_ apiPath: String, params: [String: String], completion:@escaping (Bool, String, Error?) -> Void) {
    // Create POST request
    if let requestURL = URL( string: apiPath ) {
        print("requestUrl \(apiPath)")
        // Create POST request
        var request = URLRequest( url: requestURL )
        request.httpMethod = "POST"

        var postVars : [String : String ] = params
        var postString = postVars.toHttpArgString()

        request.httpBody = postString.data( using: String.Encoding.utf8, allowLossyConversion: true )

        let sendTask = ViewController.session.dataTask( with: request) {
            (data, response, error) in

            if let nserror = error as NSError? {
                // There was an error
                // Log it or whatever
                completion(false, "", error)
                return
            }

            // Here you handle getting data into a suitable format

            let resultString = "whatever you got from api call"
            // Send it back to the completion block
            completion(true, resultString, nil)
        }
        sendTask.resume()
    }
}
// I assume you have a text field with the user name you want to try
@IBOutlet weak var usernameToCheck : UITextField!
@IBAction func usernameChecker(_ sender: Any) {
    guard let username = usernameToCheck.text else {
        // This is unlikely to happen but just in case.
        return
    }
    httpPost("https://someapicall", params: ["username" : username] ) {
        (success, value, error) in
        // This code gets called when the http request returns data.
        // This does not happen on the main thread.
        if success {
            if value == "usernameExists" {
                // User name already exists. Choose a different one.
                DispatchQueue.main.async {
                    // put code here if you need to do anything to the UI, like alerts, screen transitions etc.
                }
            }
            else if value == "usernameAvailable" {
                // You can use this user name
                DispatchQueue.main.async {
                    // put code here if you need to do anything to the UI, like alerts, screen transitions etc.
                }
            }
            else {
                // Unexpected response from server
            }

        }
        else {
            // Something did not work
            // alert "Unable to connect to server"
        }

    }
}

To make this code work you will need this:

// Syntatic sugar to convert [String:String] to http arg string
protocol ArgType {}
extension String: ArgType {}
extension Dictionary where Key: ArgType,  Value: ArgType  {
    // Implement using a loop
    func toHttpArgString() -> String {
        var r = String()
        for (n, v) in self {
            if !r.isEmpty { r += "&" }
            r += "\(n)=\(v)"
        }
        return r
    }

}
ryantxr
  • 4,119
  • 1
  • 11
  • 25