I'm trying to play around with a COVID dataset from Github (link in code below) but when I run the code nothing appears in the console. There are no errors appearing.
Can anyone advise on whats wrong here? Thanks in advance!
struct country: Decodable {
var location: String
var new_cases: Double
var people_fully_vaccinated: Double
}
func getJSON(){
guard let url = URL(string: "https://raw.githubusercontent.com/owid/covid-19-data/68c39808d445fe90b1fe3d57b93ad9be20f796d2/public/data/latest/owid-covid-latest.json") else{
return
}
let request = URLRequest(url: url)
URLSession.shared.dataTask(with: request){ (data, response, error) in
if let error = error{
print(error.localizedDescription)
return
}
guard let data = data else{
return
}
let decoder = JSONDecoder()
guard let decodedData = try? decoder.decode([country].self, from: data) else{
return
}
let countries = decodedData
for country in countries{
print (country.location)
}
}.resume()
}
getJSON()
You need
struct Root: Decodable {
var location: String
var new_cases: Double? // make it optional as it has some objects with nil
var people_fully_vaccinated: Double? // make it optional as it has some objects with nil
}
With
do {
let res = try decoder.decode([String:Root].self, from: data)
let locations = Array(res.values).map { $0.location }
print(locations)
}
catch {
print(error)
}
Related
I am creating a new version of an existing app and want to use the new async await
format for a web request. Placing a break at the JSONDecoder().decode line I see that I do have data -
but the decoding does not work. (The url and my key DO work in the old version)
Here's the JSON format of the web source (shortened - there are many more items in
a fuel_station):
{
"station_locator_url":"https://afdc.energy.gov/stations/",
"total_results":110,
"station_counts":{},
"fuel_stations":[
{
"access_code":"public",
"access_days_time":"24 hours daily; call 866-809-4869 for Clean Energy card",
"access_detail_code":"KEY_ALWAYS",
"cards_accepted":"CleanEnergy",
"date_last_confirmed":"2021-09-10",
}
]
}
I created the following models from the above:
enum CodingKeys: String, CodingKey {
case fuelStations = "fuel_stations"
case accessCode = "access_code"
case accessDaysTime = "access_days_time"
case accessDetailCode = "access_detail_code"
case cardsAccepted = "cards_accepted"
case dateLastConfirmed = "date_last_confirmed"
}
struct TopLevel: Codable {
let fuelStations: [FuelStation]
}
struct FuelStation: Codable {
let accessCode, accessDaysTime, accessDetailCode, cardsAccepted: String
let dateLastConfirmed: String
let id: String
}
I put a simplified version of the initial view in one file for testing:
struct SiteListView: View {
#State private var fuelStations: [FuelStation] = []
#State private var topLevel: TopLevel = TopLevel(fuelStations: [])
var body: some View {
NavigationView {
VStack {
List(fuelStations, id: \.id) { item in
VStack {
Text(item.accessCode)
Text(item.accessDaysTime)
}
}
}
.navigationTitle("Site List View")
.task {
await loadData()
}
}//nav
}
func loadData() async {
//I believe the DEMO_KEY in the url will allow limited retrievals
guard let url = URL(string: "https://developer.nrel.gov/api/alt-fuel-stations/v1.json?api_key=DEMO_KEY") else {
print("Invalid URL")
return
}
do {
let (data, response) = try await URLSession.shared.data(from: url)
guard (response as? HTTPURLResponse)?.statusCode == 200 else { return }
print("response status code is 200")
if let decodedResponse = try? JSONDecoder().decode(TopLevel.self, from: data) {
topLevel = decodedResponse
print("in decoding: topLevel.fuelStations.count is \(topLevel.fuelStations.count)")
//I would iterate through topLevel here and add to the fuelStations
//array but I never get here
}
} catch {
print("Invalid Data")
}
}//load data
}//struct
Any guidance would be appreciated. Xcode 13.2.1 iOS 15.2
First you should remove ? from try? for the catch to work when there is a problem in decoding like this
func loadData() async {
//I believe the DEMO_KEY in the url will allow limited retrievals
guard let url = URL(string: "https://developer.nrel.gov/api/alt-fuel-stations/v1.json?api_key=DEMO_KEY") else {
print("Invalid URL")
return
}
do {
let (data, response) = try await URLSession.shared.data(from: url)
guard (response as? HTTPURLResponse)?.statusCode == 200 else { return }
print("response status code is 200")
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
let decodedResponse = try decoder.decode(TopLevel.self, from: data)
print("in decoding: topLevel.fuelStations.count is \(decodedResponse.fuelStations.count)")
//I would iterate through topLevel here and add to the fuelStations
//array but I never get here
} catch {
print(error)
}
}
After you do this , you'll find that some attributes in your struct are coming null in response so you should change string to string? to finally be
struct TopLevel: Codable {
let fuelStations: [FuelStation]
}
struct FuelStation: Codable {
let accessCode, accessDaysTime, accessDetailCode, cardsAccepted,dateLastConfirmed: String?
let id: Int
}
In addition note use of
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
instead of hard-coding the enum
First time I'm trying to parse JSON data. The output comes out -
Course (success: zero, timestamp: nil, base: nil, date: nil, courses: nil)
Why is there nil everywhere?
I tried to change the value in the "Course" and "Currency" structures but did not lead to success
import UIKit
import JavaScriptCore
struct Course: Decodable {
var success: Bool?
var timestamp: Int?
var base: String?
var date: String?
var rates: Currency?
}
struct Currency: Decodable {
var USD: Float
var AUD: Double
var CAD: Double
var PLN: Double
var MXN: Double
}
class JsonViewContoller: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let urlData: String = "http://data.fixer.io/api/latest?access_key=7ac2982c82da926b787fd2f089b110e5&symbols=USD,AUD,CAD,PLN,MXN&format=1"
guard let url = URL(string: urlData) else { return }
URLSession.shared.dataTask(with: url) { (data, response, error) in
guard let data = data else { return }
guard error == nil else { return }
do {
let course = try JSONDecoder().decode(Course.self, from: data)
print(Course())
} catch let error {
print(error)
}
}.resume()
}
}
I have run your code you don't need to use two structures to parse the above JSON data. You can easily get Data in a single Structure. I have modified your code as follows:-
import UIKit
struct Course: Codable {
let success: Bool?
let timestamp: Int?
let base, date: String?
let rates: [String: Double]?
}
class JsonViewContoller: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
courseData()
}
fileprivate func courseData(){
let urlData: String = "http://data.fixer.io/api/latest?access_key=7ac2982c82da926b787fd2f089b110e5&symbols=USD,AUD,CAD,PLN,MXN&format=1"
guard let url = URL(string: urlData) else { return }
URLSession.shared.dataTask(with: url) { (data, response, error) in
guard let data = data else { return }
guard error == nil else { return }
do {
let course = try JSONDecoder().decode(Course.self, from: data)
print(course)
} catch let error {
print(error)
}
}.resume()
}
}
I'm trying to decode data from this massive JSON Array https://coronavirus-19-api.herokuapp.com/countries I've had luck decoding by country or using the total stat worldwide https://coronavirus-19-api.herokuapp.com/all
by doing the following
//
// GlobalSwiftViewController.swift
// Universal
//
// Created by JOE on 3/20/20.
import UIKit
final class StatSwiftViewController: UIViewController {
// THESE LABELS WILL RETRIEVE THE FOLLOWING DATA FROM THE URL: THE CASE , DEATH AND RECOVERED DATA
#IBOutlet weak var CaseLable: UILabel!
#IBOutlet weak var DeathLable: UILabel!
#IBOutlet weak var RecoveredLabel: UILabel!
struct JSONTest: Decodable {
let cases: Double
let deaths: Float
let recovered: Int?
}
override func viewDidLoad() {
super.viewDidLoad()
let urlString = "https://coronavirus-19-api.herokuapp.com/all"
guard let url = URL(string: urlString) else { return }
URLSession.shared.dataTask(with: url) { (data, response, error) in
if error != nil {
print(error!.localizedDescription)
}
guard let data = data else { return }
do {
//Decode data
let urlString = try JSONDecoder().decode(JSONTest.self, from: data)
let numberFormatter = NumberFormatter()
numberFormatter.numberStyle = .decimal
//HERE WE ARE SHOWING TO THE USER THE DATA FROM THE URL ABOVE
DispatchQueue.main.async {
self.CaseLable.text = numberFormatter.string(for: urlString.cases)
self.DeathLable.text = numberFormatter.string(for: urlString.deaths)
self.RecoveredLabel.text = numberFormatter.string(for: urlString.recovered)
//self.timeLabel.text = JSONTest.deaths
}
} catch let jsonError {
print(jsonError)
}
}.resume()
}
}
Now I'm trying to decode all of the data in this URL https://coronavirus-19-api.herokuapp.com/countries to show in one view controller, I've had success by using the single URL https://coronavirus-19-api.herokuapp.com/countries/china for the country using the same code above by just adding more vars and labels However, I'm not able to add more counties by adding each URL for each country or using the main URL for all countries https://coronavirus-19-api.herokuapp.com/countries Therefore, How can I struct all Array List using the URL for all countries?
note: Im trying to edit/update my code above to get the results as possible without installing extra pods or files...
Try to adapt your model to be able to decode the countries data.
You can test this in a Playground:
import Foundation
struct JSONTestElement: Codable {
let country: String
let cases, todayCases, deaths, todayDeaths: Int
let recovered, active, critical, casesPerOneMillion: Int
}
typealias JSONTest = [JSONTestElement]
func decode() {
let urlString = "https://coronavirus-19-api.herokuapp.com/countries"
guard let url = URL(string: urlString) else { return }
URLSession.shared.dataTask(with: url) { (data, response, error) in
if error != nil {
print(error!.localizedDescription)
}
guard let data = data else { return }
do {
//Decode data
let countriesData = try JSONDecoder().decode(JSONTest.self, from: data)
let china = countriesData.filter({ $0.country.contains("China")})
print("China data: \(china)")
} catch let jsonError {
print(jsonError)
}
}.resume()
}
decode()
So I have the following code printing out the following
[DRN1.Data(track: DRN1.Trackinfo(title: "Charly\'s Ballad (Original Mix)", artist: "Castle Queenside", imageurl: "covers.drn1.com.au/az_B1017197_Disc 1 Traxsource Nu Disco & Indie Dance_Castle Queenside.jpg"))]
However when I go to write
print(nowplaying.data.track.title)
I get errors and it won't even attempt to load the swift app
struct Nowplayng: Decodable{
let data: [Data]
}
struct Data: Decodable{
let track: Trackinfo
}
struct Trackinfo: Decodable {
let title: String
let artist: String
let imageurl: String
}
works
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
let jsonURLString = "https://api.drn1.com.au/station/playing"
guard let feedurl = URL(string: jsonURLString) else { return }
URLSession.shared.dataTask(with: feedurl) { (data,response,err)
in
guard let data = data else { return }
do{
let nowplaying = try JSONDecoder().decode(Nowplayng.self, from: data)
print(nowplaying.data)
}catch let jsonErr{
print("error json ", jsonErr)
}
// let dataAsString = String(data:data, encoding: .utf8)
// print(dataAsString)
}.resume()
}
does not work
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
let jsonURLString = "https://api.drn1.com.au/station/playing"
guard let feedurl = URL(string: jsonURLString) else { return }
URLSession.shared.dataTask(with: feedurl) { (data,response,err)
in
guard let data = data else { return }
do{
let nowplaying = try JSONDecoder().decode(Nowplayng.self, from: data)
print(nowplaying.data.track.title)
}catch let jsonErr{
print("error json ", jsonErr)
}
// let dataAsString = String(data:data, encoding: .utf8)
// print(dataAsString)
}.resume()
}
data is an array you need to loop over it
nowplaying.data.forEach {
print($0.track.title)
}
If you care about the first item do
if let item = nowplaying.data.first {
print(item.track.title)
}
I get a critical error stating the following. I've tried everything but I can't seem to access the Movie struct as it says the parent 'Type' has no member called 'data', even though it clearly does.
"Value of type '[Type?]' has no member 'data'"
MODEL
struct SearchData: Decodable {
let data: [Type?]
}
struct Type: Decodable {
let data: [Movie?]
}
struct Movie: Decodable {
let title: String?
}
CONTROLLER
fileprivate var searchResults = [Movie?]()
func fetchTitles() {
let urlString = "https://www.what-song.com/api/search?limit=10&field=america"
guard let url = URL(string: urlString) else { return }
URLSession.shared.dataTask(with: url) { (data, response, err) in
// if error occurs
if let err = err {
print("Failed to fetch titles", err)
return
}
// if success
guard let data = data else { return }
do {
let searchResult = try JSONDecoder().decode(SearchData.self, from: data)
self.searchResults = searchResult.data.data
print(searchResult)
} catch {
print("Failed to decode JSON:", error)
}
}.resume()
}
Try this :
var movieTitles = [String]()
for type in searchResult.data {
for movie in type.data {
guard let title = movie.title else { return }
print(title)
movieTitles.append(title)
}
}
you are doing a small mistake here I think
searchResult.data
will return you an array of
Type
You need to parse that array as well, something like this
searchResults = (searchResult.data[0]?.data)!