How to put data to Label from JSON - json

I need to put data that I get with JSON to cityLabel, temperatureLabel, weatherLabel for today and tableview where I Have dateLabel, weatherLabel and min and max temperature label for next 5 days. I know that for table view I need also add UITableViewDataSource. On this moment in Simulator I get clean label with nothing.
Code that I have in WeatherForecast.swift
import Foundation
// MARK: - Welcome
struct WeatherForecast: Codable {
let cod: String
let message, cnt: Int
let list: [List]
let city: City
}
// MARK: - City
struct City: Codable {
let id: Int
let name: String
let coord: Coord
let country: String
let population, timezone, sunrise, sunset: Int
}
// MARK: - Coord
struct Coord: Codable {
let lat, lon: Double
}
// MARK: - List
struct List: Codable {
let dt: Int
let main: MainClass
let weather: [Weather]
let clouds: Clouds
let wind: Wind
let sys: Sys
let dtTxt: String
let rain, snow: Rain?
enum CodingKeys: String, CodingKey {
case dt, main, weather, clouds, wind, sys
case dtTxt = "dt_txt"
case rain, snow
}
}
// MARK: - Clouds
struct Clouds: Codable {
let all: Int
}
// MARK: - MainClass
struct MainClass: Codable {
let temp, feelsLike, tempMin, tempMax: Double
let pressure, seaLevel, grndLevel, humidity: Int
let tempKf: Double
enum CodingKeys: String, CodingKey {
case temp
case feelsLike = "feels_like"
case tempMin = "temp_min"
case tempMax = "temp_max"
case pressure
case seaLevel = "sea_level"
case grndLevel = "grnd_level"
case humidity
case tempKf = "temp_kf"
}
}
// MARK: - Rain
struct Rain: Codable {
let the3H: Double
enum CodingKeys: String, CodingKey {
case the3H = "3h"
}
}
// MARK: - Sys
struct Sys: Codable {
let pod: Pod
}
enum Pod: String, Codable {
case d = "d"
case n = "n"
}
// MARK: - Weather
struct Weather: Codable {
let id: Int
let main: MainEnum
let weatherDescription, icon: String
enum CodingKeys: String, CodingKey {
case id, main
case weatherDescription = "description"
case icon
}
}
enum MainEnum: String, Codable {
case clear = "Clear"
case clouds = "Clouds"
case rain = "Rain"
case snow = "Snow"
}
// MARK: - Wind
struct Wind: Codable {
let speed: Double
let deg: Int
}
What I get in ViewController.swift by parsing JSON
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var cityNameLabel: UILabel!
#IBOutlet weak var temperatureLabel: UILabel!
#IBOutlet weak var weatherNowLabel: UILabel!
#IBOutlet weak var dailyWeatherTableView: UITableView!
var degreeSymbol = "º"
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
currentWeatherRequest()
}
func currentWeatherRequest() {
let session = URLSession.shared
let weatherURL = URL(string: "http://api.openweathermap.org/data/2.5/forecast?q=Atlanta,us?&units=metric&APPID=apikey")!
let dataTask = session.dataTask(with: weatherURL) { (data: Data?,response: URLResponse?,error: Error?) in
if let error = error {
print("Error:\n\(error)")
} else {
if let data = data {
do {
let dataString = String(data: data, encoding: String.Encoding.utf8)
print("Daily weather data:\n\(dataString!)")
let decoder = JSONDecoder()
let responseModel = try decoder.decode(WeatherForecast.self, from: data)
print(responseModel)
// } else {
// print("Error: unable to convert json data")
// }
} catch let error {
print("Error: \(error)")
}
}else {
print("Error: did not receive data")
}
}
}
dataTask.resume()
}
func weatherDetails(){
print(City.self)
}
/* func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
<#code#>
}*/
}

You can assign values to your labels after API Manager gets the values below
let responseModel = try decoder.decode(WeatherForecast.self, from: data)
print(responseModel)
by saying for example : cityNameLabel.text = responseModel.city.name
For the tableView make sure to set dailyWeatherTableView delegate and datasource using storyBoard or programmatically in view didload by writing
dailyWeatherTableView.delegate = self
dailyWeatherTableView.dataSource = self

Related

Stuck with figuring out how to use api response to make a call to retrieve a response from a different link

I'm currently having trouble with utilizing the PokeApi. I have code that allows me to view a pokemon's name and URL to the other JSON for a pokemon, but I'm not quite sure how I would retrieve that data. Here is what I have so far. And here is the link to the api! let pokeList = https://pokeapi.co/api/v2/pokemon?limit=100000&offset=0
import Foundation
// MARK: - Pokemon
struct PokemonList: Codable {
let count: Int
let results: [Result]
}
// MARK: - Result
struct Result: Codable, Identifiable {
let id = UUID()
let name: String
let url: String
enum CodingKeys: String, CodingKey {
case name, url
}
}
// MARK: - Pokemon
struct Pokemon: Codable, Identifiable {
let abilities: [Ability]
let baseExperience: Int
let forms: [Species]
let gameIndices: [GameIndex]
let height: Int
let id: Int
let isDefault: Bool
let locationAreaEncounters: String
let moves: [Move]
let name: String
let order: Int
let species: Species
let sprites: Sprites
let stats: [Stat]
let types: [TypeElement]
let weight: Int
}
// MARK: - Ability
struct Ability: Codable {
let ability: Species
let isHidden: Bool
let slot: Int
}
// MARK: - Species
struct Species: Codable {
let name: String
let url: String
}
// MARK: - GameIndex
struct GameIndex: Codable{
let gameIndex: Int
let version: Species
}
// MARK: - Move
struct Move: Codable {
let move: Species
}
// MARK: - Sprites
struct Sprites: Codable {
let backDefault: String
let backFemale: String
let backShiny: String
let backShinyFemale: String
let frontDefault: String
let frontFemale: String
let frontShiny: String
let frontShinyFemale: String
}
// MARK: - Home
struct Home: Codable {
let frontDefault: String
let frontFemale: String
let frontShiny: String
let frontShinyFemale: String
}
// MARK: - Other
struct Other: Codable {
let home: Home
let officialArtwork: OfficialArtwork
}
// MARK: - OfficialArtwork
struct OfficialArtwork: Codable {
let frontDefault: String
}
// MARK: - Stat
struct Stat: Codable {
let baseStat: Int
let effort: Int
let stat: Species
}
// MARK: - TypeElement
struct TypeElement: Codable {
let slot: Int
let type: Species
}
Code for Webservice to retrieve API Call
import Foundation
class PokeWebService: ObservableObject{
#Published var pokeList: PokemonList?
#Published var pokemonIndvl: Pokemon?
func getPokemonList() async throws {
let (data, _) = try await URLSession.shared.data(from: Constants.url.pokeList)
Task{#MainActor in
self.pokeList = try JSONDecoder().decode(PokemonList.self, from: data)
}
}
func getPokemonFromPokemonList(invlURL: String) async throws{
var plURL: URL = URL(string: invlURL)!
let (data, _) = try await URLSession.shared.data(from: plURL)
Task{#MainActor in
self.pokemonIndvl = try JSONDecoder().decode(Pokemon.self, from: data)
}
}
}
My Content View
import SwiftUI
struct PokeListView: View {
#EnvironmentObject var pokeWebService: PokeWebService
var body: some View {
List(pokeWebService.pokeList?.results ?? []){ pokemon in // <-- here
Text(pokemon.name)
Text(pokemon.url)
}
.task {
do{
try await pokeWebService.getPokemonList()
} catch{
print("---> task error: \(error)")
}
}
}
}
struct PokeListView_Previews: PreviewProvider {
static var previews: some View {
PokeListView()
.environmentObject(PokeWebService())
}
}
Since I'm still fairly new to working with APIs in Swift, how would I retrieve the data from the Pokemon.url?
Thank you!
try something like this approach (note the different model structs). You will need to find from the server docs, which fields are optional and adjust the various structs:
import Foundation
import SwiftUI
struct ContentView: View {
#StateObject var pokeWebService = PokeWebService()
var body: some View {
PokeListView().environmentObject(pokeWebService)
}
}
struct PokeListView: View {
#EnvironmentObject var pokeWebService: PokeWebService
var body: some View {
NavigationStack {
List(pokeWebService.pokeList?.results ?? []){ pokemon in
NavigationLink(pokemon.name, value: pokemon.url)
}
.navigationDestination(for: String.self) { urlString in
PokeDetailsView(urlString: urlString)
}
}
.environmentObject(pokeWebService)
.task {
do {
try await pokeWebService.getPokemonList()
} catch{
print("---> PokeListView error: \(error)")
}
}
}
}
struct PokeDetailsView: View {
#EnvironmentObject var pokeWebService: PokeWebService
#State var urlString: String
var body: some View {
VStack {
Text(pokeWebService.pokemonIndvl?.name ?? "no name")
Text("height: \(pokeWebService.pokemonIndvl?.height ?? 0)")
// ... other info
}
.task {
do {
try await pokeWebService.getPokemon(from: urlString)
} catch{
print("---> PokeDetailsView error: \(error)")
}
}
}
}
class PokeWebService: ObservableObject{
#Published var pokeList: PokemonList?
#Published var pokemonIndvl: Pokemon?
func getPokemonList() async throws {
let (data, _) = try await URLSession.shared.data(from: Constants.url.pokeList)
Task{#MainActor in
self.pokeList = try JSONDecoder().decode(PokemonList.self, from: data)
}
}
func getPokemon(from urlString: String) async throws {
if let url = URL(string: urlString) {
let (data, _) = try await URLSession.shared.data(from: url)
Task{#MainActor in
self.pokemonIndvl = try JSONDecoder().decode(Pokemon.self, from: data)
}
}
}
}
// using https://app.quicktype.io/
// MARK: - PokemonList
struct PokemonList: Codable {
let count: Int
let results: [ListItem] // <-- don't use the word Result
}
// MARK: - ListItem
struct ListItem: Codable, Identifiable {
let id = UUID()
let name: String
let url: String
enum CodingKeys: String, CodingKey {
case name, url
}
}
struct HeldItem: Codable {
let item: Species
let versionDetails: [VersionDetail]
enum CodingKeys: String, CodingKey {
case item
case versionDetails = "version_details"
}
}
struct VersionDetail: Codable {
let rarity: Int
let version: Species
}
// MARK: - Pokemon
struct Pokemon: Codable, Identifiable {
let abilities: [Ability]
let baseExperience: Int
let forms: [Species]
let gameIndices: [GameIndex]
let height: Int
let heldItems: [HeldItem]
let id: Int
let isDefault: Bool
let locationAreaEncounters: String
let moves: [Move]
let name: String
let order: Int
let pastTypes: [String]
let species: Species
let sprites: Sprites
let stats: [Stat]
let types: [TypeElement]
let weight: Int
enum CodingKeys: String, CodingKey {
case abilities
case baseExperience = "base_experience"
case forms
case gameIndices = "game_indices"
case height
case heldItems = "held_items"
case id
case isDefault = "is_default"
case locationAreaEncounters = "location_area_encounters"
case moves, name, order
case pastTypes = "past_types"
case species, sprites, stats, types, weight
}
}
// MARK: - Ability
struct Ability: Codable {
let ability: Species
let isHidden: Bool
let slot: Int
enum CodingKeys: String, CodingKey {
case ability
case isHidden = "is_hidden"
case slot
}
}
// MARK: - Species
struct Species: Codable {
let name: String
let url: String
}
// MARK: - GameIndex
struct GameIndex: Codable {
let gameIndex: Int
let version: Species
enum CodingKeys: String, CodingKey {
case gameIndex = "game_index"
case version
}
}
// MARK: - Move
struct Move: Codable {
let move: Species
let versionGroupDetails: [VersionGroupDetail]
enum CodingKeys: String, CodingKey {
case move
case versionGroupDetails = "version_group_details"
}
}
// MARK: - VersionGroupDetail
struct VersionGroupDetail: Codable {
let levelLearnedAt: Int
let moveLearnMethod, versionGroup: Species
enum CodingKeys: String, CodingKey {
case levelLearnedAt = "level_learned_at"
case moveLearnMethod = "move_learn_method"
case versionGroup = "version_group"
}
}
// MARK: - GenerationV
struct GenerationV: Codable {
let blackWhite: Sprites
enum CodingKeys: String, CodingKey {
case blackWhite = "black-white"
}
}
// MARK: - GenerationIv
struct GenerationIv: Codable {
let diamondPearl, heartgoldSoulsilver, platinum: Sprites
enum CodingKeys: String, CodingKey {
case diamondPearl = "diamond-pearl"
case heartgoldSoulsilver = "heartgold-soulsilver"
case platinum
}
}
// MARK: - Versions
struct Versions: Codable {
let generationI: GenerationI
let generationIi: GenerationIi
let generationIii: GenerationIii
let generationIv: GenerationIv
let generationV: GenerationV
let generationVi: [String: Home]
let generationVii: GenerationVii
let generationViii: GenerationViii
enum CodingKeys: String, CodingKey {
case generationI = "generation-i"
case generationIi = "generation-ii"
case generationIii = "generation-iii"
case generationIv = "generation-iv"
case generationV = "generation-v"
case generationVi = "generation-vi"
case generationVii = "generation-vii"
case generationViii = "generation-viii"
}
}
// MARK: - Sprites
class Sprites: Codable {
let backDefault: String
let backFemale: String?
let backShiny: String
let backShinyFemale: String?
let frontDefault: String
let frontFemale: String?
let frontShiny: String
let frontShinyFemale: String?
let other: Other?
let versions: Versions?
let animated: Sprites?
enum CodingKeys: String, CodingKey {
case backDefault = "back_default"
case backFemale = "back_female"
case backShiny = "back_shiny"
case backShinyFemale = "back_shiny_female"
case frontDefault = "front_default"
case frontFemale = "front_female"
case frontShiny = "front_shiny"
case frontShinyFemale = "front_shiny_female"
case other, versions, animated
}
}
// MARK: - GenerationI
struct GenerationI: Codable {
let redBlue, yellow: RedBlue
enum CodingKeys: String, CodingKey {
case redBlue = "red-blue"
case yellow
}
}
// MARK: - RedBlue
struct RedBlue: Codable {
let backDefault, backGray, backTransparent, frontDefault: String
let frontGray, frontTransparent: String
enum CodingKeys: String, CodingKey {
case backDefault = "back_default"
case backGray = "back_gray"
case backTransparent = "back_transparent"
case frontDefault = "front_default"
case frontGray = "front_gray"
case frontTransparent = "front_transparent"
}
}
// MARK: - GenerationIi
struct GenerationIi: Codable {
let crystal: Crystal
let gold, silver: Gold
}
// MARK: - Crystal
struct Crystal: Codable {
let backDefault, backShiny, backShinyTransparent, backTransparent: String
let frontDefault, frontShiny, frontShinyTransparent, frontTransparent: String
enum CodingKeys: String, CodingKey {
case backDefault = "back_default"
case backShiny = "back_shiny"
case backShinyTransparent = "back_shiny_transparent"
case backTransparent = "back_transparent"
case frontDefault = "front_default"
case frontShiny = "front_shiny"
case frontShinyTransparent = "front_shiny_transparent"
case frontTransparent = "front_transparent"
}
}
// MARK: - Gold
struct Gold: Codable {
let backDefault, backShiny, frontDefault, frontShiny: String
let frontTransparent: String?
enum CodingKeys: String, CodingKey {
case backDefault = "back_default"
case backShiny = "back_shiny"
case frontDefault = "front_default"
case frontShiny = "front_shiny"
case frontTransparent = "front_transparent"
}
}
// MARK: - GenerationIii
struct GenerationIii: Codable {
let emerald: Emerald
let fireredLeafgreen, rubySapphire: Gold
enum CodingKeys: String, CodingKey {
case emerald
case fireredLeafgreen = "firered-leafgreen"
case rubySapphire = "ruby-sapphire"
}
}
// MARK: - Emerald
struct Emerald: Codable {
let frontDefault, frontShiny: String
enum CodingKeys: String, CodingKey {
case frontDefault = "front_default"
case frontShiny = "front_shiny"
}
}
// MARK: - Home
struct Home: Codable {
let frontDefault: String
let frontFemale: String?
let frontShiny: String
let frontShinyFemale: String?
enum CodingKeys: String, CodingKey {
case frontDefault = "front_default"
case frontFemale = "front_female"
case frontShiny = "front_shiny"
case frontShinyFemale = "front_shiny_female"
}
}
// MARK: - GenerationVii
struct GenerationVii: Codable {
let icons: DreamWorld
let ultraSunUltraMoon: Home
enum CodingKeys: String, CodingKey {
case icons
case ultraSunUltraMoon = "ultra-sun-ultra-moon"
}
}
// MARK: - DreamWorld
struct DreamWorld: Codable {
let frontDefault: String
let frontFemale: String?
enum CodingKeys: String, CodingKey {
case frontDefault = "front_default"
case frontFemale = "front_female"
}
}
// MARK: - GenerationViii
struct GenerationViii: Codable {
let icons: DreamWorld
}
// MARK: - Other
struct Other: Codable {
let dreamWorld: DreamWorld
let home: Home
let officialArtwork: OfficialArtwork
enum CodingKeys: String, CodingKey {
case dreamWorld = "dream_world"
case home
case officialArtwork = "official-artwork"
}
}
// MARK: - OfficialArtwork
struct OfficialArtwork: Codable {
let frontDefault: String
enum CodingKeys: String, CodingKey {
case frontDefault = "front_default"
}
}
// MARK: - Stat
struct Stat: Codable {
let baseStat, effort: Int
let stat: Species
enum CodingKeys: String, CodingKey {
case baseStat = "base_stat"
case effort, stat
}
}
// MARK: - TypeElement
struct TypeElement: Codable {
let slot: Int
let type: Species
}
EDIT-1:
if you have problems with the NavigationStack, use this NavigationView instead.
NavigationView {
List(pokeWebService.pokeList?.results ?? []){ pokemon in
NavigationLink(destination: PokeDetailsView(urlString: pokemon.url)) {
Text(pokemon.name)
}
}
}

The data couldn’t be read in Swift

I am new to swift . I am trying to retrieve the data form api. The data in json format. Here is the data in json format.
{
"photos": [
{
"id": 424926,
"sol": 1000,
"camera": {
"id": 22,
"name": "MAST",
"rover_id": 5,
"full_name": "Mast Camera"
},
"img_src": "http://mars.jpl.nasa.gov/msl-raw-images/msss/01000/mcam/1000ML0044631200305217E01_DXXX.jpg",
"earth_date": "2015-05-30",
"rover": {
"id": 5,
"name": "Curiosity",
"landing_date": "2012-08-06",
"launch_date": "2011-11-26",
"status": "active"
}
}
]
}
Here is the online Json viewer .
NetworkMnager code .
class NetworkManager{
func fetchData(completion: #escaping ([Rover]) -> Void) {
if let url = URL(string: "https://api.nasa.gov/mars-photos/api/v1/rovers/curiosity/photos?sol=1000&api_key=Mykey") {
URLSession.shared.dataTask(with: url) { data, urlResponse, error in
if let data = data {
do {
let result = try JSONDecoder().decode([Rover].self, from: data)
completion(result)
} catch let error {
print(error.localizedDescription)
}
}
}
.resume()
}
}
}
I am trying to display the data id and status from rover (struct ) into simulator when i try to run the data , I am getting following error into console window of the xcode.The data couldn’t be read because it isn’t in the correct format. I am sure there is somethings wrong with the struct. Here is the struct code .
import Foundation
// MARK: - Welcome
struct Welcome: Codable {
let photos: [Photo]
}
// MARK: - Photo
struct Photo: Codable {
let id, sol: Int
let camera: Camera
let imgSrc: String
let earthDate: String
let rover: Rover
enum CodingKeys: String, CodingKey {
case id, sol, camera
case imgSrc = "img_src"
case earthDate = "earth_date"
case rover
}
}
// MARK: - Camera
struct Camera: Codable {
let id: Int
let name: String
let roverID: Int
let fullName: String
enum CodingKeys: String, CodingKey {
case id, name
case roverID = "rover_id"
case fullName = "full_name"
}
}
// MARK: - Rover
struct Rover: Codable {
let id: Int
let name, landingDate, launchDate, status: String
enum CodingKeys: String, CodingKey {
case id, name
case landingDate = "landing_date"
case launchDate = "launch_date"
case status
}
}
Table view controller code .
class ViewController: UIViewController {
#IBOutlet weak var tableView: UITableView!
private var posts = [Rover]()
private let networkManager = NetworkManager()
private var rowSelected = 0
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
setUpUI ()
fetchData()
}
/* override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "showDetail" {
let destination = segue.destination as? SecondViewController
destination?.posts = posts
destination?.rowSelected = rowSelected
}
}*/
private func setUpUI () {
tableView.dataSource = self
tableView.delegate = self
}
private func fetchData() {
networkManager.fetchData { [weak self] array in
self?.posts = array
DispatchQueue.main.async {
self?.tableView.reloadData()
}
}
}
}
extension ViewController: UITableViewDelegate {
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
rowSelected = indexPath.row
performSegue(withIdentifier: "cell", sender: nil)
}
}
extension ViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return posts.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let row = indexPath.row
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
let post = posts[row]
cell.textLabel?.text = String(post.id)
cell.detailTextLabel?.text = post.status
return cell
}
}
try using this example code to decode your json data:
(note I do not have a api_key, so I cannot test this).
EDIT-1:
here is the code I used for testing (sorry it is using SwiftUI), from this you should be
able to get your data decoded. Works for me.
class NetworkManager {
func fetchData(completion: #escaping ([Photo]) -> Void) { // <-- here
if let url = URL(string: "https://api.nasa.gov/mars-photos/api/v1/rovers/curiosity/photos?sol=1000&api_key=DEMO_KEY") {
URLSession.shared.dataTask(with: url) { data, urlResponse, error in
if let data = data {
do {
let result = try JSONDecoder().decode(NASAResponse.self, from: data) // <-- here
completion(result.photos)
} catch let error {
print(error.localizedDescription)
}
}
}
.resume()
}
}
}
struct ContentView: View {
let networkManager = NetworkManager()
#State var photos: [Photo] = []
var body: some View {
List (photos) { photo in
HStack {
Text(String(photo.rover.id))
Text(photo.rover.status.rawValue)
}
}
.onAppear {
networkManager.fetchData { thePhotos in
photos = thePhotos
}
}
}
}
struct NASAResponse: Codable {
let photos: [Photo]
}
// MARK: - Photo
struct Photo: Identifiable, Codable {
let sol, id: Int
let earthDate: String
let camera: Camera
let imgSrc: String
let rover: Rover
enum CodingKeys: String, CodingKey {
case sol, id
case earthDate = "earth_date"
case camera
case imgSrc = "img_src"
case rover
}
}
// MARK: - Camera
struct Camera: Identifiable, Codable {
let id, roverID: Int
let fullName: FullName
let name: CameraName
enum CodingKeys: String, CodingKey {
case id
case roverID = "rover_id"
case fullName = "full_name"
case name
}
}
enum FullName: String, Codable {
case chemistryAndCameraComplex = "Chemistry and Camera Complex"
case frontHazardAvoidanceCamera = "Front Hazard Avoidance Camera"
case mastCamera = "Mast Camera"
case navigationCamera = "Navigation Camera"
case rearHazardAvoidanceCamera = "Rear Hazard Avoidance Camera"
}
enum CameraName: String, Codable {
case chemcam = "CHEMCAM"
case fhaz = "FHAZ"
case mast = "MAST"
case navcam = "NAVCAM"
case rhaz = "RHAZ"
}
// MARK: - Rover
struct Rover: Identifiable, Codable {
let status: Status
let id: Int
let landingDate, launchDate: String
let name: RoverName
enum CodingKeys: String, CodingKey {
case status, id
case landingDate = "landing_date"
case launchDate = "launch_date"
case name
}
}
enum RoverName: String, Codable {
case curiosity = "Curiosity"
}
enum Status: String, Codable {
case active = "active"
}
EDIT-2:
To get all the "rover" into the post var, in your class ViewController: UIViewController, do this
for your tableView :
private func fetchData() {
networkManager.fetchData { thePhotos in
self.posts = thePhotos.map { $0.rover }
DispatchQueue.main.async {
self?.tableView.reloadData()
}
}
}

How to decode custom type inside dictionary value with JSON?

my JSON:
https://www.cbr-xml-daily.ru/daily_json.js
my code:
struct CoinData: Decodable {
let Valute: [String: CoinInfo]
}
struct CoinInfo: Decodable {
let Name: String
let Value: Double
}
if let safeData = data {
if let coinData = self.parseJSON(safeData) {
print(coinData)
}
}
func parseJSON(_ data: Data) -> [String: CoinInfo]? {
let decoder = JSONDecoder()
do {
let decodedData = try decoder.decode(CoinData.self, from: data)
return decodedData.Valute
} catch {
delegate?.didFailWithError(error: error)
return nil
}
}
In debug console following gets printed:
["PLN": CurrencyConverter.CoinInfo(Name: "X", Value: 19.6678), ...]
This way I can't reach Name and Value properties of a coin. What's wrong?
I am going to do for-loop to check if a key contains certain symbols. If it does - I will need to be able to access to both Name and Value
You don't actually need a for loop. Since coinData is a dictionary, you can use its subscript, together with optional binding to do this. For example, to check if the key "PLN" exists, and access its name and value:
if let coinInfo = coinData["PLN"] {
print(coinInfo.Name)
print(coinInfo.Value)
} else {
// "PLN" does not exist
}
StoyBoard
Code
import UIKit
import Alamofire
// MARK: - CoinData
struct CoinData: Codable {
let date, previousDate: String
let previousURL: String
let timestamp: String
let valute: [String: Valute]
enum CodingKeys: String, CodingKey {
case date = "Date"
case previousDate = "PreviousDate"
case previousURL = "PreviousURL"
case timestamp = "Timestamp"
case valute = "Valute"
}
}
// MARK: - Valute
struct Valute: Codable {
let id, numCode, charCode: String
let nominal: Int
let name: String
let value, previous: Double
enum CodingKeys: String, CodingKey {
case id = "ID"
case numCode = "NumCode"
case charCode = "CharCode"
case nominal = "Nominal"
case name = "Name"
case value = "Value"
case previous = "Previous"
}
}
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate{
var getCoinData = [CoinData]()
var coinNameArr = [String]()
var coinDataArr = [Valute]()
#IBOutlet weak var tblDataList: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
getData()
}
func getData()
{
let url = "https://www.cbr-xml-daily.ru/daily_json.js"
AF.request(url, method: .get, encoding: URLEncoding.default).responseJSON { response in
let json = response.data
do{
let decoder = JSONDecoder()
self.getCoinData = [try decoder.decode(CoinData.self, from: json!)]
let response = self.getCoinData[0]
if response.valute.count != 0 {
self.coinNameArr.removeAll()
self.coinDataArr.removeAll()
for (coinName, coinData) in response.valute {
self.coinNameArr.append(coinName)
self.coinDataArr.append(coinData)
}
self.tblDataList.reloadData()
} else {
}
}catch let err{
print(err)
}
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return coinDataArr.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell:coinTblCell = tableView.dequeueReusableCell(withIdentifier: "CellID", for: indexPath as IndexPath) as! coinTblCell
cell.accessoryType = .disclosureIndicator
cell.tintColor = .black
let rowData = coinDataArr[indexPath.row]
cell.lblName.text = rowData.name
cell.lblValue.text = String(rowData.value)
return cell
}
}
class coinTblCell: UITableViewCell {
#IBOutlet weak var lblName: UILabel!
#IBOutlet weak var lblValue: UILabel!
}

Cannot assign value of type 'MRData' to type '[F1Data]' when trying to parse JSON

I have been wrestling with this for a while. I am trying to parse a JSON Api into a UITableview. The url is Formula One API . I am using Codable rather than third party pods. Thought this might cut down on the amount of code. Although, as the API is not that straight forward it is hard to extract what I want. Basically, I want to list the current standing for drivers for a given year. In url and code I have given I have chosen 1999 as an example. I have been researching on Stackoverflow but each solution is very specific to a particular problem and I can't seem to relate to my issue. Below is the code I have.
struct MRData: Codable {
let xmlns: String?
let series: String?
let url: String?
let limit, offset, total: String?
let standingsTable: StandingsTable
enum CodingKeys: String, CodingKey {
case xmlns, series, url, limit, offset, total
case standingsTable = "StandingsTable"
}
}
struct StandingsTable: Codable {
let season: String?
let standingsLists: [StandingsList]
enum CodingKeys: String, CodingKey {
case season
case standingsLists = "StandingsLists"
}
}
struct StandingsList: Codable {
let season, round: String?
let driverStandings: [DriverStanding]
enum CodingKeys: String, CodingKey {
case season, round
case driverStandings = "DriverStandings"
}
}
struct DriverStanding: Codable {
let position, positionText, points, wins: String?
let driver: Driver
let constructors: [Constructor]
enum CodingKeys: String, CodingKey {
case position, positionText, points, wins
case driver = "Driver"
case constructors = "Constructors"
}
}
struct Constructor: Codable {
let constructorId: String?
let url: String?
let name: String?
let nationality: String?
}
struct Driver: Codable {
let driverId: String?
let url: String?
let givenName, familyName, dateOfBirth, nationality: String?
}
class f1TableViewController: UITableViewController {
var champions: [F1Data] = []
override func viewDidLoad() {
super.viewDidLoad()
// let jsonUrlString = "https://api.letsbuildthatapp.com/jsondecodable/website_description"
navigationController?.navigationBar.prefersLargeTitles = true
navigationItem.title = "Champion Drivers"
fetchJSON()
}
private func fetchJSON(){
let jsonUrlString = "https://ergast.com/api/f1/1999/driverstandings.json"
guard let url = URL(string: jsonUrlString) else { return }
URLSession.shared.dataTask(with: url) { (data, response, err) in
DispatchQueue.main.async {
if let err = err {
print("Failed to get data from url:", err)
return
}
guard let data = data else { return }
do {
let decoder = JSONDecoder()
// Swift 4.1
decoder.keyDecodingStrategy = .convertFromSnakeCase
self.champions = try decoder.decode(MRData.self, from: data)
self.tableView.reloadData()
//let season = f1Data.mrData.standingsTable.season
// let firstDriver = f1Data.mrData.standingsTable.standingsLists[0].driverStandings
// for driver in firstDriver {
//
// print("\(driver.driver.givenName) \(driver.driver.familyName)")
// }
//print(season)
} catch {
print(error)
}
}
}.resume()
}
// Uncomment the following line to preserve selection between presentations
// self.clearsSelectionOnViewWillAppear = false
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem
// MARK: - Table view data source
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return champions.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell(style: .subtitle, reuseIdentifier: "cellId")
let champion = champions[indexPath.row]
let driverName = champion.mrData.standingsTable.standingsLists[0].driverStandings
for driver in driverName {
cell.textLabel?.text = driver.driver.familyName
}
//cell.textLabel?.text =
//cell.detailTextLabel?.text = String(course.numberOfLessons)
return cell
}
}
Now I realize that the error is in the do catch block.
do {
let decoder = JSONDecoder()
// Swift 4.1
decoder.keyDecodingStrategy = .convertFromSnakeCase
self.champions = try decoder.decode(MRData.self, from: data)
self.tableView.reloadData()
and that the array F1Data cannot be a dictionary of MRData. So if I change it to the following self.champions = try decoder.decode([F1Data].self, from: data) the I get another error which is
debugDescription: "Expected to decode Array<Any> but found a dictionary instead.", underlyingError: nil)). Any help would be appreciated with this.
As Vadian correctly said that you need to decode at the root of your object. I briefly watched this video and the penny dropped! Assign the decoder to a variable and add to decode the complete structure starting at the root object.
guard let data = data else { return }
do {
let decoder = JSONDecoder()
// Swift 4.1
decoder.keyDecodingStrategy = .convertFromSnakeCase
let firstDriver = try decoder.decode(F1Data.self, from: data)
self.champions = firstDriver.mrData.standingsTable.standingsLists[0].driverStandings
self.tableView.reloadData()

Not able to parse the JSON data from F1 url

I am trying to access JSON data using the following url http://ergast.com/api/f1/1950/driverstandings.json
Now I am able to access the data with the following function.
In other words I am able to show the data in the console. The issue I am having is parsing the JSON data.
I am using Swifty JSON to parse the data and for some reason I am not able to show the data from the API.
The function below updates the UILabel on the storyboard.
Any help would be appreciated. Below is the entire code for the problem that I am what to solve. I want to add that there is year for the url is coming a UIPickerView on another view.
import UIKit
import Alamofire
import SwiftyJSON
class StandingViewController: UIViewController {
#IBOutlet weak var yearLabel: UILabel!
#IBOutlet weak var firstLabel: UILabel!
#IBOutlet weak var secondLabel: UILabel!
#IBOutlet weak var thirdLabel: UILabel!
#IBOutlet weak var fouthLabel: UILabel!
#IBOutlet weak var fifthLabel: UILabel!
var standing = ""
let standingDataModel = WeatherDataModel()
var currentUrl = ""
let SEASON_URL = "https://ergast.com/api/f1"
//let format = ".json"
override func viewDidLoad() {
super.viewDidLoad()
yearLabel.text = standing
userEnteredNewYear(standing: standing)
// Do any additional setup after loading the view.
}
//MARK: - Networking
/***************************************************************/
//Write the getStandingData method here:
func getStandingData (url: String) {
Alamofire.request(url, method: .get).responseJSON {
response in
if response.result.isSuccess {
print("Success we got the data!")
let standingJSON : JSON = JSON(response.result.value!)
print(standingJSON)
self.updateStandingData(json: standingJSON)
} else {
print("Error \(String(describing: response.result.error))")
self.yearLabel.text = "Connection Issues"
}
}
}
//Mark: JSON Parsing
func updateStandingData(json: JSON) {
if case standingDataModel.season = json["MRData"]["StandingsTable"]["season"].intValue {
standingDataModel.firstDriver = json["DriverStandings"][0]["Driver"]["driverId"].stringValue
updateUIWithStandingData()
} else {
yearLabel.text = "No data available"
}
}
//Mark user entered data.
func userEnteredNewYear(standing: String) {
currentUrl = SEASON_URL + "/" + String(standing) + "/driverstandings.json"
getStandingData(url: currentUrl)
}
func updateUIWithStandingData() {
yearLabel.text = "\(standingDataModel.season)"
firstLabel.text = standingDataModel.firstDriver
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get the new view controller using segue.destination.
// Pass the selected object to the new view controller.
}
*/
}
I'd recommend ditching SwiftyJSON. Create real model objects instead and use Swift's built-in Codable:
struct F1Data: Codable {
let mrData: MRData
enum CodingKeys: String, CodingKey {
case mrData = "MRData"
}
}
struct MRData: Codable {
let xmlns: String
let series: String
let url: String
let limit, offset, total: String
let standingsTable: StandingsTable
enum CodingKeys: String, CodingKey {
case xmlns, series, url, limit, offset, total
case standingsTable = "StandingsTable"
}
}
struct StandingsTable: Codable {
let season: String
let standingsLists: [StandingsList]
enum CodingKeys: String, CodingKey {
case season
case standingsLists = "StandingsLists"
}
}
struct StandingsList: Codable {
let season, round: String
let driverStandings: [DriverStanding]
enum CodingKeys: String, CodingKey {
case season, round
case driverStandings = "DriverStandings"
}
}
struct DriverStanding: Codable {
let position, positionText, points, wins: String
let driver: Driver
let constructors: [Constructor]
enum CodingKeys: String, CodingKey {
case position, positionText, points, wins
case driver = "Driver"
case constructors = "Constructors"
}
}
struct Constructor: Codable {
let constructorId: String
let url: String
let name: String
let nationality: String
}
struct Driver: Codable {
let driverId: String
let url: String
let givenName, familyName, dateOfBirth, nationality: String
}
do {
let f1Data = try JSONDecoder().decode(F1Data.self, from: jsonData)
let season = f1Data.mrData.standingsTable.season
let firstDriver = f1Data.mrData.standingsTable.standingsLists[0].driverStandings[0].driver.driverId
} catch {
print(error)
}