Reputation: 1
I created these requests, I don’t know if I asked them correctly. If someone can tell, they will be grateful. Here is my code. I need to search for a city in the search and so that the weather is displayed in the table. Please help and tell me how to set and where to fix it. Or did I set the functions and methods incorrectly? error
"The data couldn’t be read because it is missing."
import UIKit
class SearchViewController: UIViewController,UISearchResultsUpdating, UITableViewDelegate,UITableViewDataSource {
@IBOutlet weak var searchBar: UISearchBar!
@IBOutlet weak var tableView: UITableView!
var filteredCity: [OpenWeather] = []
// var searchCity: [OpenWeather]=[]
let wetherApi = WeatherManager()
var cityWeather = [OpenWeather]()
let netSer = NetworkService()
let searchController = UISearchController()
var isSearchBarEmpty: Bool {
return searchController.searchBar.text?.isEmpty ?? true
}
override func viewDidLoad() {
super.viewDidLoad()
searchController.searchResultsUpdater = self
navigationItem.searchController = searchController
tableView.delegate = self
tableView.dataSource = self
wetherApi.fetchCurrentWeather(city: "London")
}
func updateSearchResults(for searchController: UISearchController) {
self.filteredCity = self.cityWeather.filter { (city:OpenWeather) -> Bool in
if city.city.lowercased().contains(self.searchController.searchBar.text!.lowercased()){
return true
}else {
return false
}
}
//Update the results TableView
self.tableView.reloadData()
}
@objc func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return cityWeather.count
}
@objc(tableView:cellForRowAtIndexPath:) internal func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! CityTableViewCell
cell.cityTemp.text = cityWeather[indexPath.row].city
cell.cityTemp.text = "\(cityWeather[indexPath.row].main.tempCelsius)"
return cell
}
}
My structure weather:
import Foundation
import UIKit
struct OpenWeather: Codable {
let coord:Coordinate
let city:String
let weathertwo:[WeatherTwo]
let main: Main
}
struct Coordinate: Codable {
let lan:Float
let lot:Float
enum CodingKeys: String, CodingKey {
case lan = "lat"
case lot = "lon"
}
}
struct WeatherTwo: Codable {
let main: String
let description: String
}
struct Main: Codable {
private let temp: Double
var tempCelsius: Double {
get {
return (temp - 32) / 1.8
}
}
private let temp_min: Double
var tempCelsiusmin: Double {
get {
return (temp - 32) / 1.8
}
}
private let temp_max: Double
var tempCelsiusmax: Double {
get {
return (temp - 32) / 1.8
}
}
let pressure: Int
let humidity: Int
}
My code Api codable download:
struct WeatherManager {
//Deliberately deleted "q=London" within the url, because it needs to be customizable with a different city
func fetchCurrentWeather(city: String){
let URL_API = "https://api.openweathermap.org/data/2.5/weather?q="
let CITY_ID = city
//let URL_API_KEY = "<api_key>"
let session = URLSession(configuration: .default)
let urlString = URL_API + CITY_ID + "&appid=cfe547d810fc4ad95e8f24187c6b08da"
guard let url = URL(string: urlString) else {
print("Error building URL")
return
}
let task = session.dataTask(with: url) { (data, response, error) in
DispatchQueue.main.async {
if let error = error {
print(error.localizedDescription)
return
}
guard let data = data, let response = response as? HTTPURLResponse else {
print("Invalid data or response")
return
}
do {
if response.statusCode == 200 {
let items = try JSONDecoder().decode(OpenWeather.self, from: data)
print(items)
} else {
print("Response wasn't 200. It was: " + "\n\(response.statusCode)")
}
} catch {
print(error.localizedDescription)
}
}
}
task.resume()
}
}
Upvotes: 0
Views: 1643
Reputation: 5213
I see a few reasons this happens.
If you look at the response the API gives you, you will find that:
The keys weathertwo
and city
do not exist in the response, yet they are present in your OpenWeather
struct. You need to make them optional or provide your default values by overriding the init if you don't always expect this data to be present
Have a look at the coord
object in the response
"coord": {
"lon": -0.1257,
"lat": 51.5085
}
Compare the spelling of keys with your coord struct:
struct Coordinate: Codable {
let lan:Float
let long:Float
}
If you want to use different variable names, you need to look at using CodingKeys
However, the simplest fix I believe is changing your OpenWeather
struct to this:
struct OpenWeather: Codable {
let coord: Coordinate
let city: String?
let weathertwo: [WeatherTwo]?
let main: Main
}
and fixing coords to this:
struct Coordinate: Codable {
let lat: Float
let lon: Float
}
should get rid of the error and give you the data you need as I can see data from the Open Weather API printed on the console:
OpenWeather(coord: TestApp.Coordinate(lat: 25.2582, lon: 55.3047), city: nil, weathertwo: nil, main: TestApp.Main(temp: 300.38, temp_min: 299.31, temp_max: 301.29, pressure: 1008, humidity: 54))
There are other improvements that could be made such as using camel case for the variables with CodingKeys but that is something you can decide later.
Give this a go and see if it resolves your issue.
Update
As you rightly pointed out, you asked how to get the list of cities supported by Open Weather.
I had a quick look, it does not look like they have an API to get a list of all the cities. Their API seems to support you sending a city name and it will send back to you the weather data for that city.
However, they provide their list of cities here
I think the file history.city.list.json.gz
has all the city data which might need to be included in your app or somewhere online if you wish.
Have a look.
Upvotes: 0