API call in SwiftUI getting a blank response - json

I'm making an Xcode project involving an API call. When I make the call and try to access a certain part of the JSON file I can't seem to get it to work. When I try to print the data I fetch it just prints a blank row. I'm guessing I've done something wrong when I'm trying to accessing the data and pass it onto the travelTimevariable. You can find the JSON data by following the URL string in the code below.
I also get this message in my Xcode console and I don't know why and if it's related to the problem.
nw_protocol_get_quic_image_block_invoke dlopen libquic failed
I'm a beginner at Swift so I've been following tutorials online, but I can't find what I have done wrong in my code.
import SwiftUI
struct ContentView: View {
#State var travelTime = String()
var body: some View {
Text("Time:\(travelTime)")
Button("FetchAPI"){FetchAPI()}
}
func FetchAPI() {
let url = URL(string: "http://api.sl.se/api2/TravelplannerV3_1/trip.xml?key=40892db48b394d3a86b2439f9f3800fd&originExtId=300101416&destExtId=300101426&Date=2021-04-15&Time=08:00&searchForArrival=1")
URLSession.shared.dataTask(with: url!) {data, response, error in
if let data = data {
if let decodedJson = try? JSONDecoder().decode(JSONStructure.self, from: data) {
self.travelTime = decodedJson.Trip[0].LegList.Leg[0].Origin.time
}
}
}.resume()
print(self.travelTime)
}
}
struct JSONStructure: Decodable {
let Trip: [TripStructure]
}
struct TripStructure: Decodable {
let LegList: LegListStructure
}
struct LegListStructure: Decodable {
let Leg: [LegStructure]
}
struct LegStructure: Decodable {
let Origin: OriginStructure
}
struct OriginStructure: Decodable {
let time: String
}

Related

Value of type ' ' has no subscripts - JSON data

I have looked at the other answers for similar questions to this but can not find one that explains why this is coming up when requesting data from API. I am mostly confused because the error occurs when I copy the path from the JSON. After reading the error code and the documentation, I think it is something to do with an expected dictionary but found an array instead. I don't understand how to fix this when the data is coming from an API request.
Here is the JSON data that I am aiming to pull from:
Here is a picture of the error:
Here the code I have so far:
import Foundation
struct VaccineManager {
let vaccineURL = "https://coronavirus.data.gov.uk/api/v1/data?filters=areaType=overview&structure=%7B%22areaType%22:%22areaType%22,%22areaName%22:%22areaName%22,%22areaCode%22:%22areaCode%22,%22date%22:%22date%22,%22newPeopleVaccinatedFirstDoseByPublishDate%22:%22newPeopleVaccinatedFirstDoseByPublishDate%22,%22newPeopleVaccinatedSecondDoseByPublishDate%22:%22newPeopleVaccinatedSecondDoseByPublishDate%22,%22cumPeopleVaccinatedFirstDoseByPublishDate%22:%22cumPeopleVaccinatedFirstDoseByPublishDate%22,%22cumPeopleVaccinatedSecondDoseByPublishDate%22:%22cumPeopleVaccinatedSecondDoseByPublishDate%22%7D&format=json"
func performRequest(vaccineURL: String){
if let url = URL(string: vaccineURL) {
let session = URLSession(configuration: .default)
let task = session.dataTask(with: url) { (data, response, error) in
if error != nil {
print(error!)
return
}
if let safeData = data {
self.parseJASON(vaccineData: safeData)
}
}
task.resume()
}
}
func parseJASON(vaccineData: Data) {
let decoder = JSONDecoder()
do{
let decodedData = try decoder.decode(VaccineData.self, from: vaccineData)
print(decodedData.data[0].cumPeopleVaccinatedFirstDoseByPublishDate)
} catch {
print(error)
}
}
And this is the code from the other file I have to for the data:
import Foundation
struct VaccineData: Decodable {
let data: VaccineDataObject
}
struct VaccineDataObject: Decodable {
let cumPeopleVaccinatedFirstDoseByPublishDate: Int
let date: String
}
I hope that all makes sense. Also, please tell me if I am being too detailed.
Your struct definition is incorrect. It says there's a single value in data, but you have an array:
struct VaccineData: Decodable {
let data: VaccineDataObject
}
Should be:
struct VaccineData: Decodable {
let data: [VaccineDataObject]
}
You may find QuickType helpful here. It will take JSON and write decoding structs for you automatically. (Though it looks like you are pulling out such a small part, that it may be easier to hand-write as you have.)

Creating Decode Path from JSON Data in Swift that Includes Numbers and Hyphens?

this is relatively new to me and I've searched high and low but have been unsuccessful in finding a similar scenario.
I have retrieved some JSON Data from an API URL and have successfully decoded and output various values from this data as strings by parsing the data to a separate sheet and using structs and constants with the 'Decodable' value set. The problem I have is that one of the containers in the Json data is a hyphenated date in this format dates['2020-11-04'] so swift will not let me create a struct with this name (also this looks like an array but there are no square brackets when viewing the unformatted JSON data in a web browser).
Here is the full path to the date I want to output as a string and the URL being used (copied from a web browser using JSON Viewer Pro):
dates['2020-11-04'].countries.Afghanistan.date
https://api.covid19tracking.narrativa.com/api/2020-11-04
Here is the sheet containing my Structs and constants to decode the data:
import Foundation
//I understand the below name will not work but i've included it to show my presumed process
struct CovidData: Decodable {
let dates: dates[2020-11-04]
}
//Once again the below struct name does not work but i've included it as an example of my presumed process.
struct dates[2020-11-04]: Decodable {
let countries: countries
}
struct countries: Decodable {
let Afghanistan: Afghanistan
}
struct Afghanistan: Decodable {
let date: String
}
Here is my management sheet with my API call and JSON Parse:
import Foundation
protocol CovidDataManagerDelegate {
func didUpdateCovidData(_ covidDataManager: CovidDataManager, covid: CovidModel)
}
struct CovidDataManager {
var delegate: CovidDataManagerDelegate?
let covidURL = "https://api.covid19tracking.narrativa.com/api/2020-11-04"
func getData() {
let urlString = covidURL
performRequest(with:urlString)
}
func performRequest(with urlString: String){
if let url = URL(string: urlString){
let session = URLSession(configuration: .default)
let task = session.dataTask(with: url) { (data, response, error) in
if error != nil {
print("Error")
return
}
if let safeData = data {
if let covid = parseJSON(safeData){
self.delegate?.didUpdateCovidData(self, covid: covid)
}
}
}
task.resume()
}
func parseJSON(_ covidData: Data) -> CovidModel? {
let decoder = JSONDecoder()
do {
let decodedData = try decoder.decode(CovidData.self, from: covidData)
let date = decodedData.dates['2020-11-04'].countries.Afghanistan.date
let covid = CovidModel(date: date)
print(date)
return covid
} catch {
print("Error with JSON Parse")
return nil
}
}
}
}
I have not included my UI update sheet as mentioned before the call and decode is working perfectly fine when decoding data with a JSON path made up entirely of strings it is only this container with additional symbols and numbers I am stumped with.
Hopefully I've supplied enough information and apologies if some of the terminology isn't accurate, this is still quite new to me.
Thanks!

JSON Decoding Issues in Swift

I am trying to use the open weathermap.org api to return the "main" key in JSON. For some reason I keep getting caught up in my error around "failed to convert". I'm not exactly sure why I am failing to convert the JSON anyone have any ideas? Thank you.
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
let url = "https://api.openweathermap.org/data/2.5/weather?zip=10514&appid=#####################"
getData(from: url)
}
struct Response: Codable {
let weather: MyResult
}
struct MyResult: Codable {
let main: String
}
Below is the function being called to change access the JSON.
private func getData(from url:String) {
URLSession.shared.dataTask(with: URL(string:url)!, completionHandler: {
data, response, error in
guard let data = data, error == nil else {
print("Something went wrong")
return
}
// have data
var result: Response?
do {
result = try JSONDecoder().decode(Response.self, from: data)
} catch {
print("failed to convert")
}
guard let json = result else {
return
}
print(json.weather.main)
}).resume()
}
EDIT: This is what the json pulls up. Am I creating the structs incorrectly? if so how should I do it properly? thank you
{"coord":{"lon":-73.73,"lat":41.2},"weather":[{"id":802,"main":"Clouds","description":"scattered clouds","icon":"03n"}],"base":"stations","main":{"temp":291.67,"feels_like":288.73,"temp_min":290.93,"temp_max":292.59,"pressure":1024,"humidity":72},"visibility":10000,"wind":{"speed":5.7,"deg":130},"clouds":{"all":40},"dt":1594956028,"sys":{"type":1,"id":4403,"country":"US","sunrise":1594892168,"sunset":1594945530},"timezone":-14400,"id":0,"name":"Mount Kisco","cod":200}
In the JSON response, the property weather is an array:
{..., weather: [{...}] }
but in your model it expects a MyResult type. Change it to expect an array of [MyResult]:
struct Response: Codable {
let weather: [MyResult]
}

Trying to parse json, can't seem to parse arrays inside of an array

I've been trying to parse a part of this JSON file: https://opendata.brussels.be/api/records/1.0/search/?dataset=traffic-volume&rows=3&facet=level_of_service
I wanna get records->fields->geo_shape->coordinates but I can't seem to print these arrays inside of the "coordinates" array.. I thought it might be because the arrays inside of the coordinates do not have names, so I don't know how to make a variable for them. Got this code currently:
import UIKit
import Foundation
struct Geoshape : Codable {
let coordinates: Array<...>
}
struct Field : Codable {
let geo_shape: Geoshape
let level_of_service: String
}
struct Record: Codable {
let fields: Field
}
struct Traffic: Codable{
let records: Array<Record>
}
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
#IBAction func clickRefresh(_ sender: Any) {
guard let url = URL(string: "https://opendata.brussels.be/api/records/1.0/search/?dataset=traffic-volume&rows=3&facet=level_of_service") else { return }
let session = URLSession.shared
let task = session.dataTask(with: url){ (data, response, error) in
if let response = response {
print(response)
}
if let data = data {
let traffic = try? JSONDecoder().decode(Traffic.self, from: data)
print(traffic)
}
}
task.resume()
}
}
Clearly the Array<...> needs to be changed but I don't know to what. I've tried making an extra struct with just 1 variable (which is another array of the type Double: Double) but that does not seem to work. I was able to print everything just fine up to the point I tried to go into the coordinates array.
Anyone can help me?
Replace
let coordinates: Array<...>
with
let coordinates:[[Double]]
First of all your file in Resource contains a JSON which contains an array or Collection (typically in Swift).
One important thing: If you fail to decode an object in json you get null from all stored properties.
fail occurs when your Coding Keys does not match keys in json or type you are casting is diffrent.
In your code you fail to cast coordinates to its type. coordinates is a collection of collections of Double.
var coordinates: [[Double]]
If you want to fetch data into your models, you should conforms them to Decodable protocol which means that,JSON attributes can decode itself.
Based on Apple developer documentation:
Decodable is a type that can decode itself from an external representation.
also Codable protocol refers to Decodable and Encodable protocols. but current purpose is Decoding data.
typealias Codable = Decodable & Encodable
Your code should look like:
Swift 5
Prepared for Playground, paste this into your playground
import Foundation
struct GeoShape: Decodable {
var coordinates: [[Double]]
}
struct Field: Decodable {
var geo_shape: GeoShape
}
struct Record: Decodable {
var fields: Field
}
struct Traffic: Decodable {
var records: [Record]
}
guard let url = URL.init(string: "https://opendata.brussels.be/api/records/1.0/search/?dataset=traffic-volume&rows=3&facet=level_of_service")
else {fatalError()}
URLSession.shared.dataTask(with: url){ (data, response, error) in
if let data = data {
let traffic = try? JSONDecoder().decode(Traffic.self, from: data)
print("First coordinate is: ",traffic?.records.first?.fields.geo_shape.coordinates.first)
}
}.resume()

JSON request is not returning data

I'm trying to create a weather application. I followed a tutorial about JSON and Swift 4 Decodable, I followed the tutorial and it worked. The problem is that when I'm trying to put my own URL to make a request, it won't work.
This is my code:
class MainVC: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
let jsonUrlString: String = "https://api.darksky.net/forecast/APIKEY/37.8267,-122.4233"
private func getForecast(){
guard let url = URL(string: jsonUrlString) else { return }
URLSession.shared.dataTask(with: url) { (data, response, err) in
guard let data = data else { return }
do{
let weatherForecast = try JSONDecoder().decode(Weather.self, from: data)
print(weatherForecast)
}catch _ as NSError{
}
}.resume()
}
}
Error Log:
Error Domain=NSCocoaErrorDomain Code=4864 "Expected to decode Array<Any> but found a dictionary instead." UserInfo={NSCodingPath=(
), NSDebugDescription=Expected to decode Array<Any> but found a dictionary instead.}
How can I parse this https://darksky.net/dev/docs#forecast-request ? I only need the few things from the Daily sections but It seems like an array inside an array to me.
It looks like your Weather struct is built up incorrectly. It should looks something like this:
struct DataItem: Codable {
var summary: String
var uvIndex: Int
}
struct Info: Codable {
var summary: String
var icon: String
var data: [DataItem]
}
struct Weather: Codable {
var daily: Info
var hourly: Info
var timezone: String
}
I've left quite a few items out, but this should help you to get started.