The data couldn’t be read in Swift - json

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()
}
}
}

Related

Create object using multiple independent API calls

I am working on a practice project with the CoinGecko API. I just figured out how to successfully fetch, parse, and display data from the API, but I have ran into an issue. Currently I am only fetching a single coin, using a single URL but I would like to be able to fetch multiple (3 in this case), all at once and display them. Not sure how to proceed, or if my explanation makes sense so I have attached my code below. Thanks in advance.
CoinListViewModel
import Foundation
class CoinListViewModel {
private(set) var coin: Coin
init(coin: Coin) {
self.coin = coin
}
func getCoins(url: URL) async {
do {
let coin = try await WebService().getCoins(url: url)
self.coin = coin
} catch {
print(error)
}
}
}
struct CoinViewModel {
private let coin: Coin
init(coin: Coin) {
self.coin = coin
}
var symbol: String {
coin.symbol
}
var name: String {
coin.name
}
var price: [String: Double] {
coin.marketData.currentPrice
}
}
WebService
import Foundation
enum CoinsError: Error {
case invalidServerResponse
}
class WebService {
func getCoins(url: URL) async throws -> Coin {
let (data, response) = try await URLSession.shared.data(from: url)
guard let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 else {
throw CoinsError.invalidServerResponse
}
return try JSONDecoder().decode(Coin.self, from: data)
}
}
Constants
import Foundation
struct Constants {
struct coinURLs {
static let allCoinURLs = [
URL(string: "https://api.coingecko.com/api/v3/coins/bitcoin")!,
URL(string: "https://api.coingecko.com/api/v3/coins/ethereum")!,
URL(string: "https://api.coingecko.com/api/v3/coins/litecoin")!
]
}
}
Coin
import Foundation
// MARK: - WelcomeElement
struct Coin: Codable {
let symbol, name: String
let marketData: MarketData
enum CodingKeys: String, CodingKey {
case symbol, name
case marketData = "market_data"
}
static var DefaultCoin = Coin(symbol: " ", name: " ", marketData: MarketData(currentPrice: ["usd": 0.0]))
}
struct Tion: Codable {
let en: String
enum CodingKeys: String, CodingKey {
case en
}
}
struct MarketData: Codable {
let currentPrice: [String: Double]
enum CodingKeys: String, CodingKey {
case currentPrice = "current_price"
}
}
CoinListViewController
import Foundation
import UIKit
class CoinListViewController: UITableViewController {
private let vm = CoinListViewModel(coin: Coin.DefaultCoin)
override func viewDidLoad() {
super.viewDidLoad()
configureUI()
Task {
await getCoins()
}
}
private func configureUI() {
self.navigationController?.navigationBar.prefersLargeTitles = true
self.title = "CoinFlip"
tableView.register(UITableViewCell.self, forCellReuseIdentifier: "StockCell")
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
1
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "StockCell", for: indexPath)
let coin = vm.coin
var content = cell.defaultContentConfiguration()
content.text = coin.name
content.secondaryText = "$\(Int(coin.marketData.currentPrice["usd"] ?? 0))"
cell.contentConfiguration = content
return cell
}
private func getCoins() async {
await vm.getCoins(url: Constants.coinURLs.allCoinURLs[0])
print(vm.coin)
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
}
You can spawn multiple network calls if you make a few changes to your WebService. This will also return a partial result if fetching a coin fails.
func getCoins(urls: [URL]) async throws -> [Coin] {
return try await withThrowingTaskGroup(of: Coin.self, body: { taskGroup in
var coins = [Coin]()
urls.forEach { url in
taskGroup.addTask {
let (data, response) = try await URLSession.shared.data(from: url)
guard
let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200
else { return Coin() }
return try JSONDecoder().decode(Coin.self, from: data)
}
}
for try await coin in taskGroup {
coins.append(coin)
}
return coins
})
}

Swift Table view Fail to Load the data

I am new in swift . I am trying the display the data into tableview controller from API . Here is the api link . https://api.nasa.gov/mars-photos/api/v1/rovers/curiosity/photos?sol=1000&api_key=DEMO_KEY. I create model based on the json property . Here is the model code .
import Foundation
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
}
}
From this model I want to user Rover struct fields like id and status and display those property into table view cell .
Here is the network Manager 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=DEMO_KEY") {
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()
}
}
}
Here code for view controller .
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()
}
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
}
}
Here is the console result when i run the code , it is saying The data couldn’t be read because it isn’t in the correct format.

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!
}

I can't open the image. Parsing doesn't work

I can't open "url" in "images".
Parsing doesn't work. Please help. Thank you.
"images": [
{
"url": "https://ktar.com/wp-content/uploads/2020/03/ap_27df5153e3bf48198ebcfd40900446d6.jpg",
"width": 1280,
"height": 853,
"title": "Arizona reports 124 new coronavirus cases, five additional deaths",
"attribution": null
=======================================================
=======================================================
API
{
"path": "_news/2020-04-01-arizona-reports-124-new-coronavirus-cases-five-additional-deaths.md",
"title": "Arizona reports 124 new coronavirus cases, five additional deaths",
"excerpt": "The Arizona health department reported 124 new cases of coronavirus and five additional deaths on Wednesday morning, a day after the state's \"stay at home\" order went into effect.",
"heat": 145,
"tags": [
"US"
],
"type": "article",
"webUrl": "https://ktar.com/story/3056413/arizona-reports-124-new-coronavirus-cases-five-additional-deaths/",
"ampWebUrl": "https://ktar.com/story/3056413/arizona-reports-124-new-coronavirus-cases-five-additional-deaths/amp/",
"cdnAmpWebUrl": "https://ktar-com.cdn.ampproject.org/c/s/ktar.com/story/3056413/arizona-reports-124-new-coronavirus-cases-five-additional-deaths/amp/",
"publishedDateTime": "2020-04-01T09:09:00-07:00",
"updatedDateTime": null,
"provider": {
"name": "KTAR News",
"domain": "ktar.com",
"images": null,
"publishers": null,
"authors": null
},
"images": [
{
"url": "https://ktar.com/wp-content/uploads/2020/03/ap_27df5153e3bf48198ebcfd40900446d6.jpg",
"width": 1280,
"height": 853,
"title": "Arizona reports 124 new coronavirus cases, five additional deaths",
"attribution": null
}
],
"locale": "en-us",
"categories": [
"news"
],
"topics": [
"Coronavirus in US",
"Coronavirus",
"New Cases"
]
}
======================================================
======================================================
// SPORTNEWSVC.swift
// Hero
import Alamofire
import Kingfisher
import UIKit
class NewsVC: UICollectionViewController, UICollectionViewDelegateFlowLayout {
var news = [Article]()
override func viewDidLoad() {
super.viewDidLoad()
getArticles()
}
func getArticles() {
let parameters: Parameters = ["Subscription-Key": "3009d4ccc29e4808af1ccc25c69b4d5d"]
Alamofire.request("https://api.smartable.ai/coronavirus/news/US", parameters: parameters).responseData { (response) in
guard let data = response.data else { return }
do {
// let json = try JSONSerialization.jsonObject(with: data, options: [])
// print(json)
let topHeadlinesResponse = try JSONDecoder().decode(TopHeadlinesResponse.self, from: data)
self.news = topHeadlinesResponse.news
self.collectionView?.reloadData()
} catch {
print(error)
}
}
}
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return news.count
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "ArticleCell", for: indexPath) as? ArticleCell else { return UICollectionViewCell ()}
let article = news[indexPath.item]
cell.populate(with: article)
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let height: CGFloat = 277
let width = (collectionView.frame.width / 2) - 2
let size = CGSize(width: width, height: height)
return size
}
}
======================================================
======================================================
// Article.swift
// Hero
//
import Foundation
struct Article: Decodable {
let headline: String
let url: String?
private enum CodingKeys: String, CodingKey {
case headline = "title"
case url
}
}
======================================================
======================================================
//
// ArticleCell.swift
// Hero
import UIKit
import Kingfisher
class ArticleCell: UICollectionViewCell {
#IBOutlet weak var articleImageView: UIImageView!
#IBOutlet weak var headlieLabel: UILabel!
func populate(with article: Article){
headlieLabel.text = article.headline
if let url = article.url {
let url = URL(string: url)
articleImageView.kf.setImage(with: url)
}
}
}
======================================================
======================================================
import Foundation
struct TopHeadlinesResponse: Decodable {
let news: [Article]
}
The idea is that "images" in the JSON response you get is an array and you need to reflect this fact in Codable structure. The Decoding magic needs you to follow JSON data structure and namings: so if you meet field 'width' inside 'image' dictionary - you need to have a field named 'width' inside an 'image' field (e.g. having image as a separate struct)
you can read more about Decodables here
struct NewsApiResponse : Decodable {
let status : String
let updatedDateTime : Date?
let news : [Article]
}
struct Article : Decodable {
let id : Int
let title : String
let excerpt : String
let webUrl : String
let publishedDateTime : Date?
let images : [Image]
}
struct Image : Decodable {
let url : String
let width : Int
let height : Int
let title : String
}
*struct TopHeadLinesResponse: Codable {
let path, title, excerpt: String
let heat: Int
let tags: [String]
let type: String
let webURL, ampWebURL, cdnAmpWebURL: String
let publishedDateTime: Date
let updatedDateTime: JSONNull?
let provider: Provider
let images: [Image]
let locale: String
let categories, topics: [String]
enum CodingKeys: String, CodingKey {
case path, title, excerpt, heat, tags, type
case webURL
case ampWebURL
case cdnAmpWebURL
case publishedDateTime, updatedDateTime, provider, images, locale, categories, topics
}
}
// MARK: - Image
struct Image: Codable {
let url: String
let width, height: Int
let title: String
let attribution: JSONNull?
}
// MARK: - Provider
struct Provider: Codable {
let name, domain: String
let images, publishers, authors: JSONNull?
}
// MARK: - Encode/decode helpers
class JSONNull: Codable, Hashable {
public static func == (lhs: JSONNull, rhs: JSONNull) -> Bool {
return true
}
public var hashValue: Int {
return 0
}
public init() {}
public required init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
if !container.decodeNil() {
throw DecodingError.typeMismatch(JSONNull.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Wrong type for JSONNull"))
}
}
public func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
try container.encodeNil()
}
}
extension UIImageView {
func loadImagee(_ urlString: String?, onSuccess: ((UIImage) -> Void)? = nil) {
self.image = UIImage()
guard let string = urlString else { return }
guard let url = URL(string: string) else { return }
self.sd_setImage(with: url) { (image, error, type, url) in
if onSuccess != nil, error == nil {
onSuccess!(image!)
}
}
}
}
// try using SD Web library code is above install SDweb instead of // king fisher
// check your struct also check this answer
https://stackoverflow.com/questions/60310054/im-having-troubles-displaying-an-image-from-json-in-a-table-view*

How to put data to Label from 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