Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 7 years ago.
Improve this question
I get the error "Fatal error: Unexpectedly found nil while unwrapping an optional value". I am trying to fetch JSON as a dictionary from my server. How do I throw the error if the data is nil?
let jsonUrl = "jsonurl"
let session = NSURLSession.sharedSession()
let shotsUrl = NSURL(string: jsonUrl)
let task = session.dataTaskWithURL(shotsUrl!, completionHandler: {
(data,response,error) -> Void in
do {
let json = try NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers) // Get error
dispatch_async(dispatch_get_main_queue()) {
for newData in json as! [Dictionary<String, AnyObject>] {
// do stuff
}
}
} catch {
}
})
task.resume()
Edit: to clarify, I am testing when there is no internet connection, it should ignore the error thrown, instead it gives an error. I tried
guard let data = data else { return }
let json = try NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers)
but it says "Cannot force unwrap of optional type 'NSData'" on the let json line
Instead of guard the data parameter handle the error returned in the completion handler.
If there is no error then data can be safely unwrapped.
let task = session.dataTaskWithURL(shotsUrl!, completionHandler: {
(data,response,error) -> Void in
if error != nil {
// do proper error handling
} else {
do {
let json = try NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers)
...
And add also appropriate error handling in the catch scope. It seems to be annoying but can be very useful. ;-)
You are force unwrapping data by saying data!. This means if it expects data not to be nil, which it will always be if there is no internet connection. You at least need to check data is nil before you force unwrap, and then handle how you want your object created if it is nil.
if data != nil {
do {
let json = try NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers) // Get error
dispatch_async(dispatch_get_main_queue()) {
for newData in json as! [Dictionary<String, AnyObject>] {
// do stuff
}
}
} catch {
}
} else {
//handle object creation if data is nil
}
Related
I am calling REST API to get a response from the server and also testing for bad data type to caught in try catch block nut app still crashes with below error:
Could not cast value of type '__NSArrayM' (0x10575ee00) to 'NSDictionary' (0x10575f2d8).
let dataTask = URLSession.shared.dataTask(with: request) { data, response, error in
guard let data = data, error == nil else {
print(error!)
return
}
do {
let responseObject = try JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.mutableContainers) as! Dictionary<String,String>
print(responseObject)
} catch let jsonError {
print(jsonError)
}
}
dataTask.resume()
For this, I also found the solution that I should check data with JSON format first then use JSONSerialization.
if JSONSerialization.isValidJSONObject(data){
responseObject = try JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.mutableContainers) as! Dictionary<String,String>
}
else {
print("Error")
}
I want to know that if type cast value error not caught by the try-catch block then what is a use of try-catch block in network call. I can simply check for the JSON format and the pass the response object.
What is a good programming practice?
NOTE : Error is not an issue I just don't want my should crash an app when data type changes form dictionary to array or other than specified type.
This is not about valid JSON, you are getting this crash because your JSON at root is Array not Dictionary and you are trying to cast the result of jsonObject(with:) to Dictionary that is the reason you are getting this crash also there is no need to specify mutableContainers with Swift.
do {
let responseObject = try JSONSerialization.jsonObject(with: data, options: []) as! [[String:Any]]
print(responseObject)
} catch let jsonError {
print(jsonError)
}
Edit: If you don't know your JSON time then you can use if let like this way.
do {
let responseObject = try JSONSerialization.jsonObject(with: data, options: [])
if let responseArray = responseObject as? [[String:Any]] {
//Access array
}
else if let responseDictionary = responseObject as? [String:Any] {
//Access dictionary
}
print(responseObject)
} catch let jsonError {
print(jsonError)
}
This question already has an answer here:
Type 'Any' Has no Subscript Members in xcode 8 Swift 3 [duplicate]
(1 answer)
Closed 6 years ago.
I'm currently trying to learn Swift. I'm using APIs and JSON for this practice problem. At the print statement that tries to display the name it gives me the error:
"Type 'Any' has no subscript members".
Can someone explain to me what it means by no subscript member and what I can do to fix this problem? I've tried looking at other problems on Stack Overflow but can't come up with anyway to solve this.
override func viewDidLoad() {
super.viewDidLoad()
let url = URL(string: "https://www.googleapis.com/blogger/v3/blogs/2399953/posts?key=AIzaSyDRiJilhWCEkMeEi40ONIPI3eDWukA0mQo")
let task = URLSession.shared.dataTask(with: url!) { (data, response, error) in
if error != nil {
print("Error in URL")
} else {
do {
let jsonResults = try JSONSerialization.jsonObject(with: data!, options: JSONSerialization.ReadingOptions.mutableContainers) as AnyObject
let items = (jsonResults["items"]!)!
for item in items as! [AnyObject] {
print(item["author"]?["displayName"]!)
}
} catch {
print("Error in JSON")
}
}
}
task.resume()
}
It's a bad thing to case anything to AnyObject. BE specific about what you will get and if you are not then you can use guard statement to avoid crashing.
if let jsonResults = try JSONSerialization.jsonObject(with: data!, options: JSONSerialization.ReadingOptions.mutableContainers) as? Dictionary<String, AnyObject>{
if let items = jsonResults["items"] as? Array<Dictionary<String, AnyObject>>{
for item in items {
guard let dictAuthorDetails = item["author"] else {
continue
}
print(dictAuthorDetails["displayName"])
}
}
}
I'm working on a app in which the api is called through NSURLSession. When the Api works correctly there is no problem but when no data is received due to any error then after Serialization it throws error but the else block for it is never called
let task = session.dataTaskWithRequest(request) { (let data, let response, let error) in
do {
guard let data:NSData = data , let response: NSURLResponse = response where error == nil else {
throw error!
}
guard let json = try NSJSONSerialization.JSONObjectWithData(data, options: []) as? NSDictionary else{
print("Serialization failed") //This block never executes even if the Serialization Fails
throw JSONError.ConversionFailed
}
guard json.valueForKey("success") != nil else {
return
}
self.apidata = json
dispatch_async(dispatch_get_main_queue()){
self.tableView.reloadData()
}
print(json.valueForKey("success")!)
}
catch let error as JSONError{
self.showalertview(error.rawValue)
print(error.rawValue)
} catch let error as NSError{
print(error.debugDescription)
}
}
task.resume()
What I'm doing wrong here???
Consider:
do {
guard let json = try NSJSONSerialization.JSONObjectWithData(data, options: []) as? NSDictionary else {
// A
}
} catch {
// B
}
If NSJSONSerialization throws an error (i.e. if it wasn't really a JSON response or if the response was malformed), it will proceed directly to B and the guard statement doesn't come into play. The guard statement will only execute A if and only if (a) the NSJSONSerialization call, itself, didn't throw any errors (i.e. the JSON was well-formed); but (b) the cast to the the dictionary failed (e.g. the top level JSON object was an array instead of a dictionary). That's an extremely unlikely scenario (your server would have to accidentally return a well formed JSON response that was not a dictionary, e.g. a JSON array).
To accomplish what you want, you would use try? to make sure that NSJSONSerialization wouldn't throw any errors, itself:
do {
guard let json = try? NSJSONSerialization.JSONObjectWithData(data, options: []) as? NSDictionary else {
// A
throw JSONError.ConversionFailed
}
} catch {
// B
}
By doing this, only if A performs a throw will B be called
This question already has answers here:
How to parse JSON in Swift using NSURLSession
(5 answers)
Closed 7 years ago.
Was looking for useful tutorials for networking with Swift 2 and iOS 9, but it seems that topic has no content online. I have watched the WWDC session Networking with NSURLSession But I couldn't adapt the new API of iOS9 to have an asynchronous way to fetch and parse JSON data. What I have tried:
do {
if let url = NSURL(string: "site/api.php?option=fetchOps"),
let data = NSData(contentsOfURL: url),
let jsonResult = try NSJSONSerialization.JSONObjectWithData(data, options: []) as? [[String:AnyObject]] {
}
} catch let error as NSError {
print(error.description)
}
Please share your experience with networking on iOS9 and Swift2, any best practices maybe a Singleton class for networking and parsing maybe an ORM ?
Easy way to make a request for you:
How to make an HTTP request in Swift?
Or If you want to send request by your own:
HTTP POST error Handling in Swift 2
Converting a String to JSON Object:
Just as an example, here I've converted a NSString
First of all convert the NSString to NSDictionary
static func convert(src: NSString) -> NSDictionary {
// convert String to NSData
let data = src.dataUsingEncoding(NSUTF8StringEncoding)
var error: NSError?
// convert NSData to 'AnyObject'
let anyObj: AnyObject?
do {
anyObj = try NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions(rawValue: 0))
} catch let error1 as NSError {
error = error1
anyObj = nil
}
if(error != nil) {
// If there is an error parsing JSON, print it to the console
print("JSON Error \(error!.localizedDescription)")
//self.showError()
return NSDictionary()
} else {
return anyObj as! NSDictionary
}
}
And then, use it like this
if let name = dictionary["name"] as? String {
self.name = name
}
You can use the converted JSON to make an object like this
import Foundation
class Student {
var name="";
init(dictionary: NSDictionary) {
let _NAME = "name"
if let name = dictionary[_NAME] as? String {
self.name = name
}
}
}
oha, well ... search for async network calls for swift. You will find many things.
But yea... Create an NSMutableRequest, init eith the URL, set the method to GET or POST.
Now make:
let task = NSURLSession.sharedSession().dataTaskWithRequest(yourMutableRequest) {
data, response, error in
// do your stuff here
}
task.resume()
This will run asynchron, so you need to think about a way to handle the data. At first your response data is in the variable data this is a type of NSData. You will need this to create your dictionary:
let dictionaryOrArray = NSJSONSerialization.JSONObjectWithData(data, options:.MutableContainers, error: nil)
Maybe you need to put this in a guard or if let statemant.
Now you need to pass the data to the function caller... you can do this in many ways, i prefer success and failure blocks
You call your Request function and you provide a success and failure block. This blocks will do what should happen in the end:
let successBlock:[AnyObject]->Void = { response in
// handle your dict
}
let failureBlock:NSError->Void = { error in
// error handling
}
YourClass.fetchSomething(someURL, parameters: params, requestMethod: method, success : successBlock, failure: failureBlock)
Something like that. In your request block with data, response, error you just do the following:
If error {
failure(error)
return
}
success(dictionaryOrArray)
I'm currently developing my first iOS app using Swift 2.0 and Xcode Beta 2. It reads an external JSON and generates a list in a table view with the data. However, I'm getting a strange little error that I can't seem to fix:
Extra argument 'error' in call
Here is a snippet of my code:
let task = session.dataTaskWithURL(url!, completionHandler: {data, response, error -> Void in
print("Task completed")
if(error != nil){
print(error!.localizedDescription)
}
var err: NSError?
if let jsonResult = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: &err) as? NSDictionary{
if(err != nil){
print("JSON Error \(err!.localizedDescription)")
}
if let results: NSArray = jsonResult["results"] as? NSArray{
dispatch_async(dispatch_get_main_queue(), {
self.tableData = results
self.appsTableView!.reloadData()
})
}
}
})
The error is thrown at this line:
if let jsonResult = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: &err) as? NSDictionary{
Can someone please tell me what I'm doing wrong here?
With Swift 2, the signature for NSJSONSerialization has changed, to conform to the new error handling system.
Here's an example of how to use it:
do {
if let jsonResult = try NSJSONSerialization.JSONObjectWithData(data, options: []) as? NSDictionary {
print(jsonResult)
}
} catch let error as NSError {
print(error.localizedDescription)
}
With Swift 3, the name of NSJSONSerialization and its methods have changed, according to the Swift API Design Guidelines.
Here's the same example:
do {
if let jsonResult = try JSONSerialization.jsonObject(with: data, options: []) as? [String:AnyObject] {
print(jsonResult)
}
} catch let error as NSError {
print(error.localizedDescription)
}
Things have changed in Swift 2, methods that accepted an error parameter were transformed into methods that throw that error instead of returning it via an inout parameter. By looking at the Apple documentation:
HANDLING ERRORS IN SWIFT:
In Swift, this method returns a nonoptional result and is marked with the throws keyword to indicate that it throws an error in cases of failure.
You call this method in a try expression and handle any errors in the catch clauses of a do statement, as described in Error Handling in The Swift Programming Language (Swift 2.1) and Error Handling in Using Swift with Cocoa and Objective-C (Swift 2.1).
The shortest solution would be to use try? which returns nil if an error occurs:
let message = try? NSJSONSerialization.JSONObjectWithData(receivedData, options:.AllowFragments)
if let dict = message as? NSDictionary {
// ... process the data
}
If you're also interested into the error, you can use a do/catch:
do {
let message = try NSJSONSerialization.JSONObjectWithData(receivedData, options:.AllowFragments)
if let dict = message as? NSDictionary {
// ... process the data
}
} catch let error as NSError {
print("An error occurred: \(error)")
}
This has been changed in Swift 3.0.
do{
if let responseObj = try JSONSerialization.jsonObject(with: results, options: .allowFragments) as? NSDictionary{
if JSONSerialization.isValidJSONObject(responseObj){
//Do your stuff here
}
else{
//Handle error
}
}
else{
//Do your stuff here
}
}
catch let error as NSError {
print("An error occurred: \(error)") }