Reputation: 99
I'm trying to retrieve a 5 day forecast using the OpenWeatherMap API, I'm not sure why but each time I call my weatherCount() method it returns nil.
In the view model I use a print statement to verify the number of rows should be 40. I have tried to use guard statements and force unwrapping which just crashes the program. I tried implementing callback methods but don't think I did them correctly.
WeatherViewModel
import Foundation
class WeatherViewModel {
var weatherInfo: WeatherData?
weak var delegate: WeatherDelegate?
func getWeatherData() {
let weather = "https://api.openweathermap.org/data/2.5/forecast?q=London,GB&appid=fe3e0ecae7e573d25b37542f96f66f1a"
guard let url = URL(string: weather) else {
print("Could not reach the API endpoint") // this guard is not being hit
return }
let task = URLSession.shared.dataTask(with: url) { (data, response, error) in //data task object, completion handler(trailing closure)
DispatchQueue.main.async {
guard error == nil else { // Checking for errors in the request
print("Error retrieved was: \(error)")
return
}
guard let weatherResponse = data else { //checks we got the data from request
print("Could not retrieve data instead got \(data)")
return }
}
do {
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .secondsSince1970
let responseData = try decoder.decode(WeatherData.self, from: data!)
DispatchQueue.main.async {
// print("Delegate method shows \(self.delegate?.didRecieve(forecast: responseData))")
self.weatherInfo = responseData
print(self.weatherInfo)
print("Number of rows in section will be : \(self.weatherInfo?.list.count ?? 1)")
}
}
catch let e as Error {
print("Error creating current weather from JSON because: \(e.localizedDescription)")
print("Error in parsing the JSON")
NSLog("Error hit when calling weather service \(e)")
}
}
task.resume()
}
func weatherCount() -> Int {
let numberOfRows = self.weatherInfo?.list.count
print("Number of rows in weatherCount : \(numberOfRows)")
return numberOfRows ?? 1
}
}
WeatherTableViewController
import UIKit
import Foundation
class WeatherTableViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
@IBOutlet var tableView: UITableView!
lazy var viewModel: WeatherViewModel = {
return WeatherViewModel()
}()
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.delegate = self
self.tableView.dataSource = self
DispatchQueue.main.async {
self.viewModel.getWeatherData()
self.tableView.reloadData()
}
}
func numberOfSections(in tableView: UITableView) -> Int {
return 5
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
//print("Number of rows in section is: \(viewModel.weatherInfo?.list.count)")
//print("Rows: \(viewModel.weatherCount())")
return viewModel.weatherCount() ?? 1
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let weatherCell = tableView.dequeueReusableCell(withIdentifier: "WeatherCell", for: indexPath)
weatherCell.textLabel?.text = " The current temperature is : \(viewModel.weatherInfo?.list[indexPath.row].main?.temp ?? 0)"
print(viewModel.weatherInfo?.list[indexPath.row].main?.temp)
return weatherCell
}
}
numberOfRowsInSection should return 40 however returns nil
Weather
import Foundation
struct WeatherData: Decodable {
let cod: String
let message: Double
let cnt: Int
let list: [Info]
}
struct Info: Decodable {
let dt: Date
let main: WeatherInfo?
}
struct WeatherInfo: Decodable {
let temp: Double
let temp_min: Double
let temp_max: Double
let pressure: Double
let sea_level: Double
let grnd_level: Double
let humidity: Int
let temp_kf: Double
}
private enum CodingKeys: String, CodingKey {
case minTemp = "temp_min"
case maxTemp = "temp_max"
case seaLevel = "sea_level"
case temp
case pressure
case groundLevel = "grnd_level"
case humidity
case temp_kf
}
Upvotes: 3
Views: 755
Reputation: 15238
Use a completion
handler to get notify from the weather data parsing and then reload tableView
as below,
func getWeatherData(_ completion: @escaping () -> Void) {
let weather = "https://api.openweathermap.org/data/2.5/forecast?q=London,GB&appid=fe3e0ecae7e573d25b37542f96f66f1a"
guard let url = URL(string: weather) else {
print("Could not reach the API endpoint") // this guard is not being hit
return }
let task = URLSession.shared.dataTask(with: url) { (data, response, error) in //data task object, completion handler(trailing closure)
guard error == nil else { // Checking for errors in the request
print("Error retrieved was: \(error)")
return
}
guard let weatherResponse = data else { //checks we got the data from request
print("Could not retrieve data instead got \(data)")
return
}
do {
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .secondsSince1970
let responseData = try decoder.decode(WeatherData.self, from: data!)
DispatchQueue.main.async {
// print("Delegate method shows \(self.delegate?.didRecieve(forecast: responseData))")
self.weatherInfo = responseData
completion()
}
}
catch let e as Error {
print("Error creating current weather from JSON because: \(e.localizedDescription)")
print("Error in parsing the JSON")
NSLog("Error hit when calling weather service \(e)")
}
}
task.resume()
}
Update viewDidLoad
as,
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.delegate = self
self.tableView.dataSource = self
self.viewModel.getWeatherData() {
self.tableView.reloadData()
}
}
Upvotes: 3