Leonel Vaccaro Leonel Vaccaro - 3 months ago 22
Swift Question

Returning data from async call function and save variables after calling webservice in swift

My code works but can not get to keep what returns me in variables.
I can only print but not as a call to store them in variables.
I attached the class to use for webservice and as I am now calling object.

class WebService{

class func llamarWebService(completionHandler: (datos:NSArray)->()){

let urlPath = "http://alertatel.com.ar/alertatel/forwarder_number?phone=0000000000"
let url = NSURL(string: urlPath)
let session = NSURLSession.sharedSession()
var arreglo:NSArray!
let task = session.dataTaskWithURL(url!,completionHandler: {data,response,error -> Void in

if (error != nil){
print(error?.localizedDescription)
}

let nsdata: NSData = NSData(data: data!)

arreglo = self.retornarDatos(nsdata)
completionHandler(datos: arreglo)


})
task.resume()
}

class func retornarDatos(nsdata: NSData)-> Array<String>{

let datos = NSString(data:nsdata,encoding: NSUTF8StringEncoding)
let partes = datos?.componentsSeparatedByString(",")
var arreglo : [String] = []
for i in partes!{
arreglo.append(i)
}

return arreglo
}

}


In view controller use this

var resultadoWebService = WebService.llamarWebService{
datos in print([datos[0],datos[1],datos[2],datos[3],datos[4]])
}


I need save for example:

var1 = datos[0]
var2 = datos[1]


Actually code show me in the console


[OK, LeoAndroid, 20, 2, 2222222222]

Rob Rob
Answer

First, there's no point in using resultadoWebService. So I'd simplify your snippet like so:

WebService.llamarWebService { datos in
    print([datos[0],datos[1],datos[2],datos[3],datos[4]])
}

Second, you asked about updating variables. Actually, though, there's no point in updating local variables, but perhaps you want to update properties of your calling object. If so, update them inside the closure, and then trigger whatever UI update (or whatever), is appropriate for the results:

var var1: String?
var var2: String?

func performRequestAndUpdateUI() {
    WebService.llamarWebService { datos in
        guard datos.count == 5 else {
            print("didn't get the number of items data I expected")
            return
        }

        guard datos[0] == "OK" else {
            print("not everything was OK")
            return
        }

        // note, you probably want to dispatch all model updates to the main queue
        // to simplify synchronization. also UI updates must happen on main queue,
        // too.

        dispatch_async(dispatch_get_main_queue()) {
            self.var1 = datos[1]
            self.var2 = datos[2]
            ...
            // do whatever UI updates you want here
        }
    }

    // but don't try to use `datos` results here, because the above runs 
    // asynchronously and we don't have the result by the time we get here 
    // in our code.
}

Now, whether datos will always have five items, and whether you want to check that datos[0] was OK is all up to you, but hopefully it illustrates the idea, namely that you're dealing with an asynchronous method, so you can't just immediately use the values passed back in the closure, but rather you have to constrain your use of them to the closure itself. And if you're updating the model and/or UI, make sure to dispatch that back to the main thread. But you cannot immediately update variables, because the response from the web service is asynchronous, and won't be called until later.


By the way, I'd suggest you get your arms around the above code/concepts, but once you do, you may want to reevaluate your web service design. I would suggest considering using JSON for the response, as the parsing process is more robust and it's easier to differentiate between fundamental web service errors and a web service that successfully returned something (whether it was "OK" or not).

Comments