I create a class and I'm getting an warning message when I try to cast an object as AnyObject.
The warning is: " Conditional cast from 'Any' to 'AnyObject' always succeeds "
How can I remove this warning from my file ?
Here is my code:
class WebServices
{
class func getRequest( urlString: String, successBlock :#escaping (_ response :AnyObject)->Void, errorMsg:#escaping (_ errorMessage :String)->Void )
{
var request = URLRequest(url: URL(string: urlString)!)
request.httpMethod = "GET"
let task = URLSession.shared.dataTask(with: request) { (data, urlResponse, error) in
DispatchQueue.main.async {
if(error == nil)
{
do {
// Here is the warning
let responseJSON = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as? AnyObject
guard let _ = responseJSON else {
errorMsg("Some error has been occurred!")
return
}
successBlock(responseJSON!)
}
catch
{
errorMsg("Some error has been occurred!")
}
}
else
{
errorMsg(error!.localizedDescription)
}
}
}
task.resume()
}
}
Thank you guys for your time if you are reading this !
This function
let responseJSON = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as? AnyObject
returns Any and you cast it to AnyObject which nearly the same , it's better to cast to the expected json content whether it's an array or dictionary
let responseJSON = try JSONSerialization.jsonObject(with: data!) as? [String:Any]
Or
let responseJSON = try JSONSerialization.jsonObject(with: data!) as? [Any]
and change the completion accordingly
Related
This is the function in which the error appears.
func LoadMapRoute(Url:String)
{
let url = URL(string: Url)
let session = URLSession(configuration: .default)
let task = session.dataTask(with: url!, completionHandler: {
(data, response, error) in
guard error == nil else {
print(error!.localizedDescription)
return
}
guard let jsonResult = try? (JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? [String: Any]),
let jsonResponse = jsonResult else {
print("error in JSONSerialization")
return
}
//Call this method to draw path on map
self.drawPath(from: polyLineString)
})
task.resume()
}
This is the line in which error is shown in guard let = jsonResult
let jsonResponse = jsonResult
Please refer below code:
guard let jsonResult = try? JSONSerialization.jsonObject(with: data!, options: .allowFragments),let jsonResponse = jsonResult as? [String: Any] else {
print("error in JSONSerialization")
return
}
Your jsonResult is already unwrapped so no need to unwrapped again.
Thanks
You dont need the statement "let jsonResponse = jsonResult". jsonResult is already checked for.
I am creating an app using Swift, where I have to display a list of Countries using below URL:
https://restcountries.eu/rest/v2/all
Here I have to fetch only name key and add to my NSMutableArray. When I am trying to parse this using URLSession, I am getting error :
expression produced error: error: /var/folders/_3/27lhgzw9699c4_yg37r72_d80000gp/T/expr40-bcf47d..swift:1:65: error: use of undeclared type 'Foundation'
Swift._DebuggerSupport.stringForPrintObject(Swift.UnsafePointer<Foundation.Data>(bitPattern: 0x105c0c2f0)!.pointee)
Below is my code:
func fetchCountryList(countryURL:URL, completion:#escaping (NSDictionary) -> ()) {
print(countryURL)
let request = NSMutableURLRequest( url: countryURL as URL)
let task = URLSession.shared.dataTask(with: request as URLRequest) {
data, response, error in
do{
if let data = data,
let jsonString = try JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.mutableContainers) as? NSDictionary
, error == nil {
completion(jsonString)
} else {
print("error=\(error!.localizedDescription)")
let errorDict = ["error_status":true,"message":error!.localizedDescription] as [String : Any]
completion(errorDict as NSDictionary)
}
}
catch{
print("error=\(error.localizedDescription)")
let errorDict = ["error_status":true,"message":error.localizedDescription] as [String : Any]
completion(errorDict as NSDictionary)
}
}
task.resume()
}
Can anyone please suggest me my mistake here. Thanks in Advance!
Replace
try JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.mutableContainers) as? NSDictionary
With
try JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.mutableContainers) as? [[String: Any]]
Since it's an array of objects, we also need to cast the json as an array
For getting an array of country names, we can do this by:
guard let countries = try JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.mutableContainers) as? [[String: Any]] else {
return
}
let countryNames = countries.map { $0["name"] as! String }
Replace
let jsonString = try JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.mutableContainers) as? NSDictionary
with
if let result = try JSONSerialization.jsonObject(with: data, options:[]) as? [[String:Any]] {
for item in result {
print(item["name"])
}
}
the response is array not dictionary also don't use NS stuff
NSDictionary => [String:Any]
NSUrl => URL
NSMutableURLRequest => URLRequest
func fetchCountryList(countryURL:URL, completion:#escaping (_ arr :[[String:Any]]?, _ error:Error?) -> ()) {
print(countryURL)
let request = URLRequest( url: countryURL)
let task = URLSession.shared.dataTask(with: request) {
data, response, error in
do{
if let data = data,
let jsonString = try JSONSerialization.jsonObject(with: data, options:[]) as? [[String:Any]]
, error == nil {
completion(jsonString,nil)
} else {
print("error=\(error!.localizedDescription)")
let errorDict = ["error_status":true,"message":error!.localizedDescription] as [String : Any]
completion(nil,error)
}
}
catch{
print("error=\(error.localizedDescription)")
let errorDict = ["error_status":true,"message":error.localizedDescription] as [String : Any]
completion(nil,error)
}
}
task.resume()
}
Call
serviceModel.fetchCountryList(countryURL:countryURL){ (result,error) in
if let res = result {
print(res)
}
}
I'm trying to loop through a multidimensional array/dictionary in Swift which I got from a JSON string. Originally I come from PHP so I'm probably a bit off with my approach.
My issue is that I can't seem to get into a sub-array/dict.
My code so far:
func getJSON(){
let url = NSURL(string: url)
URLSession.shared.dataTask(with: (url as URL?)!, completionHandler: {(data, response, error) -> Void in
if let jsonObj = try? JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? NSDictionary {
print(jsonObj!)
let array = jsonObj!.allKeys
for keys in array {
print(keys)
}
OperationQueue.main.addOperation({
//Nothing to do right now
})
}
}).resume()
}
My JSON:
{
"ArrayName1": {
"info": "This is my first array!",
"more": "Even more info!"
},
"ArrayName2": {
"info": "This is my second array!",
"more": "Even more info about the second array!"
}
}
The function prints the key (e.g. ArrayName1) which is good, but how do I get deeper into the array? To print the "info"?
If you are sure it's dictionary in this form [String: [String: Any]], you may want to try this.
if let jsonObj = try? JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? [String: [String: Any]] {
let array = jsonObj!.allKeys
for key in array {
print(key)
let info = jsonObj[key]["info"] as! String
}
}
First of all don't use NSURL in Swift 3. There is a native struct URL.
Second of all .allowFragments is useless as the JSON is clearly a collection type.
Third of all don't use NSDictionary in Swift, use native type Dictionary.
Fourth of all do the error handling at least the possible error passed by the data task.
All collection types are dictionaries. Use Fast Enumeration to parse keys and values. All keys and values are strings.
func getJSON(){
let url = URL(string: url)!
URLSession.shared.dataTask(with: url, completionHandler: {(data, response, error) -> Void in
if error != nil {
print(error!)
return
}
do {
if let jsonObj = try JSONSerialization.jsonObject(with: data!) as? [String:[String:String]] {
for (key, innerDictionary) in jsonObj {
print(key)
if let info = innerDictionary["info"] {
print("info", info)
}
}
DispatchQueue.main.async {
//Nothing to do right now
}
} else { print("The JSON object is not [String:[String:String]]") }
} catch {
print(error)
}
}).resume()
}
I have written a function for a URL request. This contains a completion handler that returns a dictionary of [String: AnyObject] that is fetched from the URL.
The code for this is:
func getDataAsyncFromURLRequest(url: NSURL, completion: ([String : AnyObject]) -> ()) {
let request = NSMutableURLRequest(URL: url)
let session = NSURLSession.sharedSession()
let task = session.dataTaskWithRequest(request) { (data, response, error) -> Void in
if error != nil {
print("error=\(error)")
return
}
else {
let datastring = NSString(data: data!, encoding: NSUTF8StringEncoding)
if let data = datastring!.dataUsingEncoding(NSUTF8StringEncoding) {
do {
let json = try NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions()) as! [String : AnyObject]
completion(json)
} catch {
print("json error: \(error)")
}
}
}
}
task.resume()
}
In some cases, however, I will receive an array of [String : AnyObject] and not the dictionary. So instead of making a duplicate function that takes the array of dictionaries as parameter for the completion handler, I though it was possible to do like this
func getDataAsyncFromURLRequest<T>(url: NSURL, completion: (T) -> ()) {
// code here
}
... and then do like this let json = try NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions()) as! T, but that gives me this error: Cannot invoke 'getDataAsyncFromURLRequest' with an argument list of type '(NSURL, completion: (_) -> ())'
What would be the best way to make the completion handler accept a parameter with a type decided at runtime, if possible at all?
It's very easy why don't you use AnyObject
func getDataAsyncFromURLRequest(url: NSURL, completion: (AnyObject) -> ()) {
let request = NSMutableURLRequest(URL: url)
let session = NSURLSession.sharedSession()
let task = session.dataTaskWithRequest(request) { (data, response, error) -> Void in
if error != nil {
print("error=\(error)")
return
}
else {
let datastring = NSString(data: data!, encoding: NSUTF8StringEncoding)
if let data = datastring!.dataUsingEncoding(NSUTF8StringEncoding) {
do {
let json = try NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions())
completion(json)
} catch {
print("json error: \(error)")
}
}
}
}
task.resume()
}
And result of JSONObjectWithData can be [AnyObject] (Array) or [String:AnyObject] and tree of those items.
So after got result, you can also check type of result in completion block
Like this
if result is [String:AnyObject]
...
else if result is [AnyObject]
...
else
//throw error : because then it is not JSON
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")
}