I'm trying to use NSURLSession to get ID's from a url, but the code between let task = NSURLSession.... and task.resume is never executed (I placed a breakpoint inside to check, as you can see in the attached image).
What am I missing?
func getPlayingSongData() {
while loopItem < IDsForSongsToPlay.count {
self.getPlayingSongDataLoop()
loopItem++
}
self.stream()
}
func getPlayingSongDataLoop() {
playerStatus = "Paused"
let songIDToGet = IDsForSongsToPlay[loopItem]
let url:NSURL = NSURL(string: "http://url.com/ajax.php?call=song&id=\(songIDToGet)")!
print(url)
let task = NSURLSession.sharedSession().dataTaskWithURL(url) { (data:NSData?, response:NSURLResponse?, error:NSError?) -> Void in
// Convert Json data into an Array
let songID:[String:String] = (try! NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers)) as! [String:String]
print(songID["id"]!)
self.StreamIDsForSongs.append((songID["id"]!))
songPlaying = songID["title"]!
}
task.resume()
}
UPDATE:
I changed
let task = NSURLSession.sharedSession().dataTaskWithURL(url) { (data:NSData?, response:NSURLResponse?, error:NSError?) -> Void in
to
let task = NSURLSession.sharedSession().dataTaskWithURL(url) { (data, response, error) in
and then it worked for me.
My code with breakpoints
According to Apple the function dataTaskWithURL(_:completionHandler:):
Creates a task that retrieves the contents of the specified URL, then calls a handler upon completion.
If the request completes successfully, the data parameter of the completion handler block contains the resource data, and the error parameter is nil. If the request fails, the data parameter is nil and the error parameter contain information about the failure.
Then you should see the values for each parameter regarding your request.
I hope this help you.
Related
i have a Swift code for retrieving and parsing JSON data from the web. everything seems to work fine except one problem i am facing right now. I tried to solve it for some time, have check all sources online.
I have created global dictionary "dicOfNeighbours" that would like to return as a result of parse to other class by calling "func startConnection".
dicOfNeighbours stores parsed data till it goes out of the closing bracket of the:
"let task = session.dataTaskWithRequest(urlRequest) { ... }"
After it stores just nil. And returned result is nil as well.
I have tried to pass "dicOfNeighbours" variable by reference using inout and it is still returns nil result.
there might some solution that i missed.
I would appreciate any help and suggestions.
Thanks!
var dicOfNeighbours = Dictionary<String, [Int]>()
func startConnection() -> Dictionary<String, [Int]>{
let requestURL: NSURL = NSURL(string: "http://www....data.json")!
let urlRequest: NSMutableURLRequest = NSMutableURLRequest(URL: requestURL)
let session = NSURLSession.sharedSession()
let task = session.dataTaskWithRequest(urlRequest) {
(data, response, error) -> Void in
let httpResponse = response as! NSHTTPURLResponse
let statusCode = httpResponse.statusCode
if (statusCode == 200) {
do{
let json = try NSJSONSerialization.JSONObjectWithData(data!, options:.AllowFragments)
if let neighbours = json["neighbours"] as? [String: Array<Int>] {
var i = 0
for (index, value) in neighbours {
self.dicOfNeighbours[index] = value
}
}
}catch {
print("Error with Json: \(error)")
}
}
}
task.resume()
return self.dicOfNeighbours
}
You are using return instead of using a callback. You are doing your parsing when the network connection is done; asynchronously.
To synchronize it, you'd need to use semaphores, but that is highly discouraged on the main thread.
Instead, do the appropriate things with the result when your completion block is executed. Think of the data task as 'do stuff, come back to me when you're done'.
I am trying to create HTTP request with Swift2 and to return response outside of nested function. My code looks like this:
let session = NSURLSession.sharedSession()
let dataTask = session.dataTaskWithRequest(request, completionHandler: { (data, response, error) -> Void in
if (error != nil) {
print(error)
} else {
let httpResponse = response as? NSHTTPURLResponse
print(httpResponse)
print(data)
// return data
}
I would like to return data variable outside of nested function. Or to have some other variable defined before nested function, which I can set inside of nested function. Like this:
var test = "";
// some nested function
let dataTask = session.dataTaskWithRequest(request, completionHandler: { (data, response, error) -> Void in
test = "test"
})
Anybody has some suggestion for this problem?
If you want to synchronously wait until the data task has finished so that you can return the fetched data, you have to use a semaphore.
func getDataSynchronously(request: NSURLRequest) -> NSData? {
var returnData: NSData?
let semaphore = dispatch_semaphore_create(0)
let dataTask = NSURLSession.sharedSession().dataTaskWithRequest(request, completionHandler: { (data, response, error) in
returnData = data
dispatch_semaphore_signal(semaphore)
})
dataTask.resume()
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER)
return returnData
}
The semaphore will force the calling thread to stop and wait until it is signaled upon completion of the data task. Calling the above function would look like this
let request = NSURLRequest(URL: NSURL(string: "https://www.google.com")!)
let data = getDataSynchronously(request)
print("Synchronously fetched \(data!.length) bytes")
On the other hand, if you want to kick-off the data task in the background and be asynchronously notified about its completion, you can add your own completion block to the function's signature.
func getDataAsynchronously(request: NSURLRequest, completion: NSData? -> ()) {
let dataTask = NSURLSession.sharedSession().dataTaskWithRequest(request, completionHandler: { (data, response, error) in
completion(data)
})
dataTask.resume()
}
Calling the asynchronous method could look like this
let request = NSURLRequest(URL: NSURL(string: "https://www.google.com")!)
getDataAsynchronously(request) { data in
print("Asynchronously fetched \(data!.length) bytes")
}
I am currently trying to learn about Swift 2.0 and OAuth integration via this sample application: https://github.com/soundcloud/iOSOAuthDemo
The following snippet below is causing me problems and causing the application to fail in its compilation.
private func requestMe(token: String) {
let url = NSURL(string: "https://api.soundcloud.com/me.json?oauth_token=\(token)")!
let request = NSURLRequest(URL: url)
let session = NSURLSession(configuration: NSURLSessionConfiguration.defaultSessionConfiguration(),
delegate: nil, delegateQueue: NSOperationQueue.mainQueue())
let dataTask = session.dataTaskWithURL(url) { (data, response, error) -> Void in
if let jsonOutput = NSJSONSerialization.JSONObjectWithData(data, options: nil, error: nil) as? [String:AnyObject] {
self.displayMe(jsonOutput)
}
}
dataTask.resume()
}
However when compiling my error handling looks as though it has changed in this version of Swift (2.0) and is causing the following error:
Extra argument 'error' in call with the compilation.
I have reviewed the following stack posting on this issue: Swift: Extra argument 'error' in call
and adjusted my code to try and correct the error handling as such:
let dataTask = session.dataTaskWithURL(url) { (data, response, error) -> Void in
if let jsonOutput = NSJSONSerialization.JSONObjectWithData(data, options: nil) as? [String:AnyObject] {
self.displayMe(jsonOutput)
}
}
catch let error as NSError {
print(error);}
dataTask.resume()
}
I have also tried changing:
(data, options: nil, error: nil)
to
(data:NSData?, error:NSError?)
However neither of these resolving the issue. Can someone guide me as to what is probably a silly mistake I am making with this error handling.
Thanks in advance!,
There were several problems with your code: you added catch but forgot do and try. Also you can't pass nil as an option parameter anymore for NSJSONSerialization, and you have to safely unwrap the data optional.
Here's a fixed version:
let dataTask = session.dataTaskWithURL(url) { (data, response, error) -> Void in
do {
if let myData = data, let jsonOutput = try NSJSONSerialization.JSONObjectWithData(myData, options: []) as? [String:AnyObject] {
self.displayMe(jsonOutput)
}
} catch let error as NSError {
print(error)
}
}
dataTask.resume()
I am getting this error while trying to write a function which returns an NSDictionary after reading data using JSON:
Cannot convert the expression's type NSDictionary? to type Void
func readJsonData() -> NSDictionary{
let urlPath = "http://www.telize.com/geoip"
let url = NSURL(string: urlPath)
let session = NSURLSession.sharedSession()
let task = NSURLSession.sharedSession().dataTaskWithURL(url!, completionHandler:{(data , response, error) -> Void in
if (error != nil){
println(error)
}else{
let jsonresult = NSJSONSerialization.JSONObjectWithData(data, options: .MutableContainers, error: nil) as? NSDictionary
return jsonresult
}
})
task.resume()
}
Your completionHandler has a return type of Void but you are returning jsonresult which is of type NSDictionary.
You should remove the line:
return jsonresult
As you should not return a value to a completion handler.
If you want to set a variable in your class on the completion, you can execute a thread on the main queue and set the variable equal to jsonresult:
let task = NSURLSession.sharedSession().dataTaskWithURL(url!, completionHandler:{(data , response, error) -> Void in
if (error != nil){
println(error)
}else{
let jsonresult = NSJSONSerialization.JSONObjectWithData(data, options: .MutableContainers, error: nil) as? NSDictionary
dispatch_async(dispatch_get_main_queue(), {
self.jsonData = jsonresult
//Update GUI, etc.
})
}
})
You'll need to learn how an asynchronous call works. Your function readJsonData cannot work that way.
You call dataTaskWithURL. The call returns immediately, without any result. That's why readJsonData cannot return a dictionary. However, the data that dataTaskWithURL downloads arrives a long time later (100s of milliseconds, or seconds, or maybe a minute). When the data arrives, your callback function is called. The callback cannot return anything because its call has long since returned. The callback must deliver the data to whoever wants it.
I am new to iOS development and need help on below issue.
I have the below code that downloads a JSON data from web and populates an array with that.
let urlPath = "……………………………"
let url = NSURL(string: urlPath)
let session = NSURLSession.sharedSession()
let task = session.dataTaskWithURL(url!, completionHandler: {data, response, error -> Void in
if (error != nil) {
println(error)
} else {
let jsonResult: AnyObject = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: nil)!
dispatch_async(dispatch_get_main_queue()) {
for var i = 0; i < jsonResult.count; i++ {
self.sales[i] = jsonResult[i]["daily_sales"] as NSString
}
}
}
})
task.resume()
println(self.sales[0])
at the end the app crashes since it does not wait for the JSON data to be downloaded.
What are the alternative ways to handle this?
It crashes because of this println(self.sales[0]). You trying to display sales that are not even downloaded. Put this line at the end of your completion block and you should be OK