Parsing JSON returns nil, why? - ios

This is my first project working with JSON, so this question could probably be relevant to others in the same situation.
I'm making a weather app using the DarkSky API. So far, I'm requesting the data from the internet, parsing it and, for testing, printing it in the console. Unfortunately, I just get nil. Here's the relevant code:
-> Functions in my ViewController:
func getWeatherData(latitude: String, longitude: String, time: String) {
let basePath = "https://api.darksky.net/forecast/xxxxxxxxxxxxxxxb170/"
let url = basePath + "\(latitude),\(longitude)"
let request = URLRequest(url: URL(string: url)!)
let task = URLSession.shared.dataTask(with: request) {
(data:Data?, response:URLResponse?, error:Error?)
in
if let data = data {
do {
if let json = try JSONSerialization.jsonObject(with: data, options: []) as? [String:Any] {
let dictionary = json
UserDefaults.standard.set(dictionary, forKey: "lastWeatherUpdate")
}
} catch {
print(error.localizedDescription)
}
}
}
}
func getCurrentWeather() {
getWeatherData(latitude: "37", longitude: "40", time: "40000")
let weather = UserDefaults.standard.dictionary(forKey: "lastWeatherUpdate")
print(weather?["latitude"])
}
Does someone spot my mistake? Here's how DarkSky specifies the structure of the JSON data:
"latitude": 47.20296790272209,
"longitude": -123.41670367098749,
"timezone": "America/Los_Angeles",
"currently": {
"time": 1453402675,
"summary": "Rain",
"icon": "rain",
"nearestStormDistance": 0,
"precipIntensity": 0.1685,
"precipIntensityError": 0.0067,
"precipProbability": 1,
"precipType": "rain",
"temperature": 48.71,
"apparentTemperature": 46.93,
"dewPoint": 47.7,
"humidity": 0.96,
"windSpeed": 4.64,
"windGust": 9.86,
"windBearing": 186,
"visibility": 4.3,
"cloudCover": 0.73,
"pressure": 1009.7,
"ozone": 328.35
Well, apparently that's just the important part of the JSON.
Can anyone spot my mistake?

Does someone spot my mistake?
Actually there are two mistakes:
As mentioned in the comments the task must be resumed
dataTask works asynchronously, it requires a completion handler to be able to print something after the call.
The code uses a simple enum with associated types as return type for convenience reasons.
enum WeatherResult {
case success([String:Any]), failure(Error)
}
func getWeatherData(latitude: String, longitude: String, time: String, completion: #escaping (WeatherResult)->()) {
let basePath = "https://api.darksky.net/forecast/xxxxxxxxxxxxxxxb170/"
let urlString = basePath + "\(latitude),\(longitude)"
let url = URL(string: urlString)!
let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
if let error = error {
completion(.failure(error))
return
}
do {
if let json = try JSONSerialization.jsonObject(with: data!) as? [String:Any] {
completion(.success(json))
} else {
completion(.failure(NSError(domain: "myDomain", code: 1, userInfo: [NSLocalizedDescriptionKey : "JSON is not a dictionary"])))
}
} catch {
completion(.failure(error))
}
}
task.resume()
}
func getCurrentWeather() {
getWeatherData(latitude: "37", longitude: "40", time: "40000") { result in
switch result {
case .success(let dictionary):
UserDefaults.standard.set(dictionary, forKey: "lastWeatherUpdate")
print(dictionary["latitude"])
case .failure(let error):
print(error.localizedDescription)
}
}
}

Related

if i want to receive data from the server the app does not go to the URLSession if debug hem then skip the URLSession and i don't know why

This is the Json object which I should receive
[
{
"id": "18",
"profileName": "testProfiel24"
},
{
"id": "19",
"profileName": "testProfiel25"
},
{
"id": "1021",
"profileName": "testProfiel26"
},
{
"id": "1022",
"profileName": "testProfiel27"
},
{
"id": "1023",
"profileName": "testProfiel28"
}
]
This the function that I send, I used my api url to send an api call
func getData(){
let getToken : String? = KeychainWrapper.standard.string(forKey: "accessToken")
I add a value of the token to the url and with the print line I get the link
let url = URL(string: "http://....../profile/load")!
let queryItems = [URLQueryItem(name: "token", value: getToken!)]
let newUrl = url.appending(queryItems)!
print(newUrl)
The I make a URLSession with the new URL
but when hi goed to make dataTask. hi skips every things and goes to
resume()
URLSession.shared.dataTask(with: newUrl, completionHandler: { data, response, error in
guard let data = data,error == nil else{
print(error?.localizedDescription as Any)
return
}
var result : Profile?
do{
result = try JSONDecoder().decode(Profile.self, from: data)
}catch{
print("failed to convert\(error.localizedDescription)")
}
guard let json = result else{
return
}
print(json.id)
print(json.profileName)
}).resume()
with the debug mode I get this value of dataTask =
LocalDataTask <1F6380AD-5D01-490D-BA4A-94A7FD893531>.<2>
But I don't receive any data for the server because hi skips it
This the code to add a key and a value to the link and it works good
extension URL {
func appending(_ queryItems: [URLQueryItem]) -> URL? {
guard var urlComponents = URLComponents(url: self, resolvingAgainstBaseURL: true) else {
return nil
}
urlComponents.queryItems = (urlComponents.queryItems ?? []) + queryItems
return urlComponents.url
}
}
This the data that should receive for the server
struct Profile : Decodable {
let id, profileName : String
}

My json response is coming in different format than I expected

In browser my url gives results in perfect JSON format as follows
"articles": [
{
"source": {
"id": "the-times-of-india",
"name": "The Times of India"
},
"author": "Times Of India",
But Where as in Xcode output the response I am getting is as follows. How to convert this response into perfect json format
{
articles = (
{
author = "Times Of India";
content = "Hyderabad: Senior Police officials arrive at the site of the encounter. All four accused in the rape
description = "India News: All four accused in the rape and murder of woman veterinarian in Telangana have been killed in an encounter with the police. Cops claimed they tried t";
publishedAt = "2019-12-06T04:15:00Z";
source = {
name = "The Times of India";
};
},
I am using the following code to decode the json data
let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
guard let dataResponse = data, error == nil else {
print(error?.localizedDescription ?? "Response Error")
return
}
do{
//here dataResponse received from a network request
let jsonResponse = try JSONSerialization.jsonObject(with: dataResponse, options: [])
print(jsonResponse) //Response result
} catch let parsingError {
print("Error", parsingError)
}
}
task.resume()
Please help me with this issue.
first you must create a Decodable struct and put it before viewController class:
struct YourArrray: Decodable {
let author: String?
let content: String?
let location: String?
let description : String?
let publishedAt : String?
let name: String?
}
declare your Url:
let jsonUrlString = "https://yourUrljson"
after that create your struct array var:
var myVar = [YourArrray]()
now you can procede to decode json:
fileprivate func fetchJsonObject() {
guard let url = URL(string: jsonUrlString) else { return }
URLSession.shared.dataTask(with: url) { (data, respons, err) in
guard let data = data else { return }
do {
let jsonResponse = try JSONDecoder().decode([myVar].self, from: data)
print(jsonResponse)
} catch let jsonErr {
print("Error serializing:", jsonErr)
}
}.resume()
}
Now you can simply call the function fetchJsonObject() and you're done
Hope this help :)

How to parse this JSON response in Swift

I usually use this code to parse most of JSON responses
Before the code, here the JSON I need to get form it the "workspace"
{
"count": 1,
"next": null,
"previous": null,
"results": [{
"id": 307,
"email": "999#ios.net",
"firstName": "fighter",
"categories": [],
"workspace": 302,
"phone": "25485"
}]
}
here is my code:
func getWorkSpace() {
DispatchQueue.main.async {
let returnAccessToken: String? = UserDefaults.standard.object(forKey: "accessToken") as? String
print("UserDefaults Returned Access Token is: \(returnAccessToken!)")
let access = returnAccessToken!
let headers = [
"content-type": "application/x-www-form-urlencoded",
"cache-control": "no-cache",
"postman-token": "dded3e97-77a5-5632-93b7-dec77d26ba99",
"Authorization": "JWT \(access)"
]
let request = NSMutableURLRequest(url: NSURL(string: "https://v5/workspaces/")! as URL,
cachePolicy: .useProtocolCachePolicy,
timeoutInterval: 10.0)
request.httpMethod = "GET"
request.allHTTPHeaderFields = headers
let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
if (error != nil) {
print(error!)
} else {
if let dataNew = data, let responseString = String(data: dataNew, encoding: .utf8) {
print("--------")
print(responseString)
print("--------")
DispatchQueue.main.async {
do {
let json = try JSON(data: data!, options: .allowFragments)
let answer = json["results"]
let workspace = Int(answer["workspace"].int!)
// let workspace = Int(answer["workspace"].string!)!
// let workspace = answer["workspace"].int!
print("Workspace is: \(workspace)")
} catch {
print("Error saving workspace!")
}
}
}
}
})
dataTask.resume()
}
}
This code usually works for me, but this time it's not. Please don't suggest me to use Codables because I didn't learn them yet.
SwiftyJSON
do {
let json = try JSON(data: data1!)
let answer = json["results"].array
answer?.forEach {
print($0["workspace"].int!)
}
} catch {
print("Error saving workspace!")
}
JSONSerialization
let json = try! JSONSerialization.jsonObject(with:data, options :[]) as! [String:Any]
let results = json["results"] as! [[String:Any]]
results.forEach {
print($0["workspace"] as! Int)
}
Codable
struct Root : Codable {
let results:[Model]
}
struct Model: Codable {
let id: Int
let email, firstName: String
let workspace: Int
let phone: String
}
let res = try! JSONDecoder().decode(Root.self, from:data)
print(res.results)

How can I decode JSON with array and more JSON inside?

I recently began with swift. I need decode the json on below.
The JSON has inside two more JSON the first one (validation) does not matter. The second one (result) has a JSON array inside (serviceCenter). I need the information of each servicenter. I try to use servicecenter as decodeable class to get a servicenter object, but as the JSON does not have the proper format I can't do it.
[
{
"validation": {
"bValid": true,
"sDescription": "Access true."
}
},
{
"result": {
"serviceCenter": [
{
"model": "Vanquish",
"color": "Purple",
"make": "Aston Martin",
"sTag": "3666",
"sVin": "6JDO2345",
"sMiles": "3666",
"bDamage": "1",
"dDateTime": "04-17-2018 9:38 AM"
},
{
"model": "F360",
"color": "Red",
"make": "Ferrari",
"sTag": "0010",
"sVin": "6JDO2347",
"sMiles": "80000",
"bDamage": "1",
"dDateTime": "04-17-2018 9:25 AM"
},
{
"model": "Vanquish",
"color": "Purple",
"make": "Aston Martin",
"sTag": "0009",
"sVin": "6JDO2345",
"sMiles": "25000",
"bDamage": "1",
"dDateTime": "04-17-2018 9:23 AM"
},
{
"model": "Vanquish",
"color": "Purple",
"make": "Aston Martin",
"sTag": "0003",
"sVin": "6JDO2345",
"sMiles": "20000",
"bDamage": "1",
"dDateTime": "04-12-2018 10:37 AM"
}
]
}
}
]
I try so much but i cant do it.
This its my code now, Could someone help me please
do {
let parseoDatos = try JSONSerialization.jsonObject(with: data!) as! [AnyObject]
let h = type(of: parseoDatos )
print("'\(parseoDatos)' of type '\(h)'")
let contenido = parseoDatos[1]["result"]
if let services = contenido!! as? Dictionary<String, Array<Any>> {
for (_,serviceArray) in services {
for sc in serviceArray{
let h = type(of: sc )
print("'\(sc)' of type '\(h)'")
}
}
}
} catch {
print("json processing failed")
}
the result of print sc is
{
bDamage = 1;
color = Purple;
dDateTime = "04-17-2018 9:38 AM";
make = "Aston Martin";
model = Vanquish;
sMiles = 3666;
sTag = 3666;
sVin = 6JDO2345;
}' of type '__NSDictionaryI'
You can use Codable
Initial response have array of InitialElement and InitialElement is is struct with validation , result , result may be nil
don't forget to add your URL at url
URLSession.shared.dataTask(with: url!) { (data, response, error) in
if let initial = try? JSONDecoder().decode([InitialElement].self, from: data){
// inital now have array of InitialElement and InitialElement is is struct with validation , result , result may be nil
print(initial)
}
}.resume()
With this Model for Data:
import Foundation
struct InitialElement: Codable {
let validation: Validation?
let result: ResultData?
}
struct ResultData: Codable {
let serviceCenter: [ServiceCenter]
}
struct ServiceCenter: Codable {
let model, color, make, sTag: String
let sVin, sMiles, bDamage, dDateTime: String
}
struct Validation: Codable {
let bValid: Bool
let sDescription: String
}
extension InitialElement {
init(data: Data) throws {
self = try JSONDecoder().decode(InitialElement.self, from: data)
}
}
try this code
enum ParsingError: Error {
case wrongFormat(String)
}
do {
let jsonObject = try JSONSerialization.jsonObject(with: data!)
guard let array = jsonObject as? [Any] else {
throw ParsingError.wrongFormat("wrong root object")
}
guard array.count == 2 else {
throw ParsingError.wrongFormat("array count != 2")
}
guard let dict = array[1] as? [String: Any] else {
throw ParsingError.wrongFormat("can't parse dict from array")
}
guard let serviceCenters = (dict["result"] as? [String: Any])?["serviceCenter"] else {
throw ParsingError.wrongFormat("can't parse serviceCenters")
}
guard let serviceCentersArray = serviceCenters as? [[String : Any]] else {
throw ParsingError.wrongFormat("serviceCenters is not an array")
}
print("\(type(of: serviceCentersArray))\n", serviceCentersArray)
} catch {
print("json processing failed: \(error)")
}
Thanks all for yours answers, it's my first question here, and i feels great all the help. Finally i can resolve the problem. Maybe not the best way, but here its the code:
let task = URLSession.shared.dataTask(with: request) { (data: Data?, response: URLResponse?, error: Error?) in
if error != nil{
print("error=\(String(describing: error))")
return
}
do {
let parseoDatos = try JSONSerialization.jsonObject(with: data!) as! [AnyObject]
let h = type(of: parseoDatos )
print("'\(parseoDatos)' of type '\(h)'")
let contenido = parseoDatos[1]["result"]
if let services = contenido!! as? Dictionary<String, Array<Any>> {
for (_,serviceArray) in services {
for sc in serviceArray{
let h = type(of: sc )
print("'\(sc)' of type '\(h)'")
let valid = JSONSerialization.isValidJSONObject(sc) // true
print(valid)
do {
let jsonData = try JSONSerialization.data(withJSONObject: sc, options: .prettyPrinted)
let serviceDecoded = try JSONSerialization.jsonObject(with: jsonData, options: [])
if let scJSON = serviceDecoded as? [String:String] {
print ("--------------------------")
print("'\(scJSON)' of type '\(type(of: scJSON))'")
print ("--------------------------")
}
} catch {
print(error.localizedDescription)
}
i think later y try to use Codable as suggested, but for now the code is working ok. Thanks again!
//try this it is working
let arrayMain=try?JSONSerialization.jsonObject(with:jsonData!,options:.mutableLeaves) as! Array<Any>
//print(arrayMain!)
if let firstDictionart=arrayMain![0] as? [String: Any] {
if let insideFirstDict = firstDictionart["validation"] as? [String: Any]{
print(insideFirstDict["bValid"]!)
print( insideFirstDict["sDescription"]!)
}
}
if let resultDictionary=arrayMain![1] as? [String: Any] {
if let serviceDictionary = resultDictionary["result"] as? [String: Any] {
for array in serviceDictionary["serviceCenter"] as! Array<Any>{
if let infoDicti=array as? [String: Any] {
print( infoDicti["color"]!)
print( infoDicti["make"]!)
print( infoDicti["color"]!)
print( infoDicti["sTag"]!)
print( infoDicti["sVin"]!)
print( infoDicti["sMiles"]!)
print( infoDicti["sVin"]!)
print( infoDicti["model"]!)
print( infoDicti["bDamage"]!)
print( infoDicti["dDateTime"]!)
}
}
}
}

Why is function returning empty string when called? [duplicate]

I am a beginner in iOS development with Swift language. I have a JSON file contains the data as below.
{
"success": true,
"data": [
{
"type": 0,
"name": "Money Extension",
"bal": "72 $",
"Name": "LK_Mor",
"code": "LK_Mor",
"class": "0",
"withdraw": "300 $",
"initval": "1000 $"
},
{
},
{
},
]
}
I want to parse this file and have to return the dictionary which contain the data in the JSON file. This is the method I wrote.
enum JSONError: String, ErrorType {
case NoData = "ERROR: no data"
case ConversionFailed = "ERROR: conversion from JSON failed"
}
func jsonParserForDataUsage(urlForData:String)->NSDictionary{
var dicOfParsedData :NSDictionary!
print("json parser activated")
let urlPath = urlForData
let endpoint = NSURL(string: urlPath)
let request = NSMutableURLRequest(URL:endpoint!)
NSURLSession.sharedSession().dataTaskWithRequest(request) { (data, response, error) -> Void in
do {
guard let dat = data else {
throw JSONError.NoData
}
guard let dictionary: NSDictionary = try NSJSONSerialization.JSONObjectWithData(dat, options:.AllowFragments) as? NSDictionary else {
throw JSONError.ConversionFailed
}
print(dictionary)
dicOfParsedData = dictionary
} catch let error as JSONError {
print(error.rawValue)
} catch {
print(error)
}
}.resume()
return dicOfParsedData
}
When I modify this method to return a dictionary, it always return nil. How can I modify this method.
You can not return for an asynchronous task. You have to use a callback instead.
Add a callback like this one:
completion: (dictionary: NSDictionary) -> Void
to your parser method signature:
func jsonParserForDataUsage(urlForData: String, completion: (dictionary: NSDictionary) -> Void)
and call the completion where the data you want to "return" is available:
func jsonParserForDataUsage(urlForData: String, completion: (dictionary: NSDictionary) -> Void) {
print("json parser activated")
let urlPath = urlForData
guard let endpoint = NSURL(string: urlPath) else {
return
}
let request = NSMutableURLRequest(URL:endpoint)
NSURLSession.sharedSession().dataTaskWithRequest(request) { (data, response, error) -> Void in
do {
guard let dat = data else {
throw JSONError.NoData
}
guard let dictionary = try NSJSONSerialization.JSONObjectWithData(dat, options:.AllowFragments) as? NSDictionary else {
throw JSONError.ConversionFailed
}
completion(dictionary: dictionary)
} catch let error as JSONError {
print(error.rawValue)
} catch let error as NSError {
print(error.debugDescription)
}
}.resume()
}
Now you can use this method with a trailing closure to get the "returned" value:
jsonParserForDataUsage("http...") { (dictionary) in
print(dictionary)
}

Resources