I am trying to write a function that will execute an asynchronous GET request, and return the response (as any data type, but here it is as NSData).
This question is based on: How to use NSURLConnection completionHandler with swift
func getAsynchData() -> NSData {
var dataOutput : NSData
let url:NSURL = NSURL(string:"some url")
let request:NSURLRequest = NSURLRequest(URL:url)
let queue:NSOperationQueue = NSOperationQueue()
NSURLConnection.sendAsynchronousRequest(request, queue: queue, completionHandler:{ (response: NSURLResponse!, data: NSData!, error: NSError!) -> Void in
/* this next line gives the below error */
dataOutput = data
})
return dataOutput
}
but I get an error:
error: variable 'dataOutput' captured by a closure before being initialized
I have tried returning the value from the completionHandler, but it requires a void return, which eerily reminds me of my hope to solve this problem without help... :D
I have looked at: How to use completionHandler Closure with return in Swift? but this does not really answer my question. My objective here is to get the data from my asynchronous request out of the block, for use elsewhere in my code. Am I supposed to do all of the work with this request in this block and not get the data out?
Thank you!
EDIT
ok so I have an option which I think might work, but it doesn't seem right to me. Can someone tell me if this is the best way of accomplishing my goal?
func doThingsWithData( data: NSData ) -> String {
/* code to extract string from NSData */
return somestring
}
func getAsynchData() {
let url:NSURL = NSURL(string:"some url")
let request:NSURLRequest = NSURLRequest(URL:url)
let queue:NSOperationQueue = NSOperationQueue()
NSURLConnection.sendAsynchronousRequest(request, queue: queue, completionHandler:{ (response: NSURLResponse!, data: NSData!, error: NSError!) -> Void in
/* this next line gives the below error */
doThingsWithData(data)
})
}
EDIT 2 -> responding to undo
Thanks, Undo. Your answer makes sense to me. Here is more of the puzzle. I have a class that is my API handler. I want to be able to instantiate that class, and call a function on it to get data from the API. I would rather get all the data with one api call, rather than making separate calls each time for each value I need to get out, as a single API call contains all the data I need, but that might be a whole other answer. Here is the code:
class GetInfoFromAPI {
func getSpecificValue(index : String) -> String {
/* I assume I need to send the values from this function, yea? but how do I get them here? */
}
func doThingsWithData( data: NSData ) -> String {
/* code to extract string from NSData */
var error: NSError?
let jsonDict = NSJSONSerialization.JSONObjectWithData(data, options: nil, error: &error) as NSDictionary
specificValue1 : String = jsonDict.valueForKey("value1") as String
specificValue2 : String = jsonDict.valueForKey("value2") as String
specificValue3 : String = jsonDict.valueForKey("value3") as String
/* I want to get these ^^^ values into the ViewController below */
}
func getAsynchData() {
let url:NSURL = NSURL(string:"some url")
let request:NSURLRequest = NSURLRequest(URL:url)
let queue:NSOperationQueue = NSOperationQueue()
NSURLConnection.sendAsynchronousRequest(request, queue: queue, completionHandler:{ (response: NSURLResponse!, data: NSData!, error: NSError!) -> Void in
/* this next line gives the below error */
doThingsWithData(data)
})
}
}
class ViewController: UIViewController {
@IBOutlet var labelVariable1: UILabel
@IBOutlet var labelVariable2: UILabel
@IBOutlet var labelVariable3: UILabel
let apiInstance = GetInfoFromAPI()
@IBAction func buttonTapped(sender : AnyObject) {
labelVariable1 = apiInstance.getSpecificValue(1)
labelVariable2 = apiInstance.getSpecificValue(2)
labelVariable3 = apiInstance.getSpecificValue(3)
}
}
Thank you for taking time to answer my questions. I am new to swift, and stack overflow is immensely helpful!
Let me try to explain this - it's a misunderstanding of threading, one I myself struggled with at first. I'm going to try to simplify things a little bit. When you run this code:
NSLog("Log 1")
NSURLConnection.sendAsynchronousRequest(request, queue: queue, completionHandler:{ (response: NSURLResponse!, data: NSData!, error: NSError!) -> Void in
NSLog("Log in Completion Block")
})
NSLog("Log after request")
You're going to get output that looks like this:
Log 1
Log after request
Log in completion block
Let me make a chart, kind of a timeline:
"Log 1" (before request is sent)
|
|
Request sent over Internet
|
/ \
"Log after request" |
This is logged *before* |
the other one, because this |
one doesn't have to wait for |
the network to respond. |
|
The method finishes and returns a value. |
------------------------------------------------------------------------------
| The network finally responds,
| and the completion block is run.
|
"Log in completion block"
The vertical line is where the method finishes and returns. In all cases, your method will have already returned a value to its caller before your completion block is run. You can't think about this linearly.
Am I supposed to do all of the work with this request in this block and not get the data out?
Yes, essentially. I can help more if you show me the code that calls getAsynchData()
in the first place.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With