Swift 2.0 do block not executing in dataTaskWithURL - ios

I grabbed a version of this code from this site (thanks to Sergey A. Novitsky). However, the 'do' block is never executed so the "json" variable is always nil. What's going on? I'm using Xcode 7.0 Beta.
func getJson(url:NSURL) -> NSDictionary! {
var json:NSDictionary!
let session = NSURLSession.sharedSession()
let task = session.dataTaskWithURL(url) {
(data:NSData?, response:NSURLResponse?, error:NSError?) in
do {
json = try NSJSONSerialization.JSONObjectWithData(data!, options: .AllowFragments) as? NSDictionary
} catch let caught as NSError {
print(caught)
} catch {
// Something else happened.
let error: NSError = NSError(domain: "<Your domain>", code: 1, userInfo: nil)
print(error)
}
}
task.resume()
return json
}

As mentioned in the other answers you need a completion block like this
func getJson(url:NSURL, completion: (json:NSDictionary?, error:NSError?)->()) {
let session = NSURLSession.sharedSession()
let task = session.dataTaskWithURL(url) {
(data:NSData?, response:NSURLResponse?, error:NSError?) in
do {
let json = try NSJSONSerialization.JSONObjectWithData(data!, options: .AllowFragments) as? NSDictionary
completion(json: json, error:nil)
} catch let caught as NSError {
completion(json: nil, error:caught)
} catch {
// Something else happened.
let error: NSError = NSError(domain: "<Your domain>", code: 1, userInfo: nil)
completion(json: nil, error:error)
}
}
task.resume()
}
call the function with
getJson(NSURL(string:"http://myserver.com")!) { (json, error) -> () in
if error != nil {
print(error!)
} else {
print(json!)
// do something with the json dictionary
}
}

That's not how asynchronous functions work. json is nil when it's returned because it won't actually be set until the asynchronous completion block for dataTaskWithURL is called. To get the value out of getJson, pass in a completion block of your own and call it inside the task's, passing back the parsed JSON to your calling site.

Related

Using Data with ContentsOfUrl

I have this code snippet here:
let endpointURL = URL(string: "http://foobar.com")
let downloadTask = URLSession.shared.downloadTask(with: endpointURL!, completionHandler: { url, response, error in
if (error == nil) {
let dataObject = NSData(contentsOfURL: endpointURL!)
let jsonArray: Array = JSONSerialization.JSONObjectWithData(dataObject!, options: nil, error: nil) as Array
}
})
downloadTask.resume()
And I'm having this issue:
Ambiguous use of 'init(contentsOfURL:)' for NSData part
How can I make it unambiguous?
I recommend to use something like this in Swift 3, to load JSON data dataTask is more appropriate than downloadTask.
let endpointURL = URL(string: "http://foobar.com")
let dataTask = URLSession.shared.dataTask(with: endpointURL!) { data, response, error in
guard error == nil else {
print(error!)
return
}
do {
// the assumed result type is `[[String:Any]]` cast it to the expected type
if let jsonArray = try JSONSerialization.jsonObject(with: data!) as? [[String:Any]] {
print(jsonArray)
}
} catch {
print(error)
}
}
dataTask.resume()

fatal error: unexpectedly found nil while unwrapping an Optional value json

When I get data from server it will display fatal error
below my code
URLSession.shared.dataTask(with: myRequest, completionHandler: { (data:Data?, response:URLResponse?, error:Error?) -> Void in
DispatchQueue.main.async {
if error != nil {
}
do {
if let json = try JSONSerialization.jsonObject(with: (data)!, options: .mutableContainers) as? NSMutableDictionary {
}
}
}
let task = URLSession.shared.dataTask(with: request) { (data, response, error) in
do
{
let dict = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers)
Dispatch.main.async {
// refresh ui like tableview[tableView.roloadData()] or collectionview
}
}catch{
}
}
task.resume()
You a have few errors in your code.
You are checking error for nil and deserializing json outside this check.
You are force unwrapping (!) data without checking it for nil.
You use do, but I don't see catch. Please don't use do/catch unless you really need it.
Also, I suggest you using mainThread when you already deserialized object.
URLSession.shared.dataTask(with: myRequest) { (data, _, error) -> Void in
guard let data = data else { return }
let dict = try? JSONSerialization.jsonObject(with: data, options: .mutableContainers) {
...
Dispatch.main.async {
// refresh ui
}
}
}

Swift 3: not working with completion handler in iOS

I have created one function using completion handler in NSObject class for consumption of web services. However I am not getting a way to call that function with handler return.
func getUser(url:String, completionHandler: #escaping (NSDictionary?, NSError?) -> ()) {
let config = URLSessionConfiguration.default // Session Configuration
let session = URLSession(configuration: config) // Load configuration into Session
let url = URL(string: url)!
let task = session.dataTask(with: url, completionHandler: {
(data, response, error) in
if error != nil {
completionHandler(nil, error as NSError?)
} else {
do {
if let json = try JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? [NSDictionary: Any] {
completionHandler(json as NSDictionary?,nil)
}
} catch {
print("error in JSONSerialization")
}
}
})
task.resume()
}
You should make sure that your completionHandler is called in every cases: for example, when the JSONSerialization throws, you catch and print the error, but you're not calling your completionHandler. The same if the JSON result is nil
ADDING
You can handle it in this way:
do {
let json = try JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? [NSDictionary: Any]
completionHandler(json,nil)
} catch(let error) {
print(error)
completionHandler(nil, error)
}

I keep getting a use unresolved identifier error swift

When I try to run this project I am greeted with a "Use of unresolved identifier error." Here is the code I get the error on the line with
var jsonDict = try NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers)
as! NSDictionary
let task : NSURLSessionDataTask = session.dataTaskWithRequest(request, completionHandler: {data, response, error -> Void in
if((error) != nil) {
print(error!.localizedDescription)
} else {
let err: NSError?
do {
var jsonDict = try NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers) as! NSDictionary
} catch {
if(err != nil) {
print("JSON Error \(err!.localizedDescription)")
}
else {
//5: Extract the Quotes and Values and send them inside a NSNotification
let quotes:NSArray = ((jsonDict.objectForKey("query") as! NSDictionary).objectForKey("results") as! NSDictionary).objectForKey("quote") as! NSArray
dispatch_async(dispatch_get_main_queue(), {
NSNotificationCenter.defaultCenter().postNotificationName(kNotificationStocksUpdated, object: nil, userInfo: [kNotificationStocksUpdated:quotes])
})
}
}
}
})
can someone please help. Thank you.
You problem could be this line of code in the catch block.
let quotes:NSArray = ((jsonDict.objectForKey("query") as! NSDictionary).objectForKey("results") as! NSDictionary).objectForKey("quote") as! NSArray
In the above statement jsonDict is out of scope. You declared jsonDict in the do block but are trying to use it in the catch block.
Try Following:-
(Assuming JSON has a root node structure)
let urlRequest: NSMutableURLRequest = NSMutableURLRequest(URL: yourURL)
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 queries = json["query"] as? [[String: AnyObject]] {
for query in queries {
if let quote = query["quote"] as? String {
self.quoteArr.append(quote)
}
}//for loop
dispatch_async(dispatch_get_main_queue(),{
// your main queue code
NSNotificationCenter.defaultCenter().postNotificationName(kNotificationStocksUpdated, object: nil, userInfo: [kNotificationStocksUpdated:quotes])
})//dispatch
}// if loop
}
catch
{
print("Error with Json: \(error)")
}
}
else
{
// No internet connection
// Alert view controller
// Alert action style default
}
this cobweb of code is exactly why SwiftyJSON library exists. I recommend it highly, it can be imported into your project using cocoapods.
using this library the resultant code would be
jsonQuery["query"]["results"]["quote"]
which is more readable and as you implement more APIs, much faster.

cannot invoke 'jsonObjectWithData'

I cannot figure out how to solve this issue.
This comes from a youtube tutorial to build a simple Weather App.
The tutorial was uploaded in March 2015 and therefor written in a previous version of Swift, there it worked, with the current Swift 2 it doesn't.
The error I get is: "cannot invoke 'jsonObjectWithData' with an argument list of type '(NSData, options: nil, error: NSError)'"
func getWeatherData(urlString:String) {
let url = NSURL(string: urlString)
let task = NSURLSession.sharedSession().dataTaskWithURL(url!){ (data, response, error) in
dispatch_async(dispatch_get_main_queue(), {
self.setLabels(data)
})
}
task.resume()
}
func setLabels(weatherData: NSData) {
var jsonError: NSError
let json = NSJSONSerialization.JSONObjectWithData(weatherData, options: nil, error: jsonError)
if let name = json["name"] as? String {
self.ResultLabel.text = name
}
}
if you want to get this code ready for Swift 2, you have to run the JSONParser with try and catch possible errors.
private func httpGetRequest(request: NSURLRequest!, callback: (NSData?, String?) -> Void) {
let session = NSURLSession.sharedSession()
let task = session.dataTaskWithRequest(request){
(data, response, error) -> Void in
if error != nil {
callback(nil, error!.localizedDescription)
} else {
callback(data, nil)
}
}
task!.resume()
}
func setLabels(weatherData: NSData) {
do {
let json = try NSJSONSerialization.JSONObjectWithData(weatherData, options: NSJSONReadingOptions.MutableContainers) as! NSDictionary
if let name = json["name"] as? String {
self.resultLabel.text = name
}
} catch {
print(error)
self.resultLabel.text = "Lorem Ipsum"
}
}
func loadWeatherData() {
let weatherRequest = NSMutableURLRequest(URL: NSURL(string: "Your URL String goes here")!)
httpGetRequest(weatherRequest){
(data, error) -> Void in
if error != nil {
print("Error: \(error)")
} else {
self.setLabels(data!)
}
}
}
Hope that will help to solve your Problems.
Try this:
var jsonError: NSError?
let json = NSJSONSerialization.JSONObjectWithData(weatherData, options: nil, error: &jsonError)
in swift 3.0 and Swift 4.2
try this ...
do {
let jsonData = try JSONSerialization.data(withJSONObject: your array or dictionary, options: JSONSerialization.WritingOptions()) as Data
let json = try JSONSerialization.jsonObject(with: jsonData as Data, options: JSONSerialization.ReadingOptions(rawValue: UInt(0)))
}
catch
{
}
You need to pass the error pointer into NSJSONSerialization.JSONObjectWithData(...) with &.
let json = NSJSONSerialization.JSONObjectWithData(weatherData, options: nil, error: &jsonError) // &jsonError points to the NSErrorPointer of the NSError object
In swift 3 you can try this:
func setLabels(weatherData: NSData) {
do {
var jsonError: NSError
let json = try JSONSerialization.jsonObject(with: weatherData as Data, options: []) as! NSDictionary
if let name = json["name"] as? String {
self.ResultLabel.text = name
}
} catch {
}
}
In Swift 3, you can fix the same error with the code below:
do{
let jsonData = try JSONSerialization.jsonObject(with: (data)!, options: JSONSerialization.ReadingOptions.mutableContainers) as! [String: AnyObject]
}catch{
print("Error while parsing JSON: Handle it")
}

Resources