Home > Blockchain >  Parsing different queries with one func using SWIFT
Parsing different queries with one func using SWIFT

Time:10-08

My problem is - I'm building Weather App that displays 20 different cities at the same time (that's the task). I can do it with one city when i put it in guard let url = URL(string: ) directly like this (London)

struct Constants {
    static let API_KEY = "<api-key>"
    static let baseURL = "https://api.openweathermap.org/data/2.5/weather?appid=\(API_KEY)&units=metric&q=" //   cityName
}
class APICaller {
    
    static let shared = APICaller()
    func getData(completion: @escaping(Result<[WeatherDataModel], Error>) -> Void) {
        guard let url = URL(string: "\(Constants.baseURL)London") else { return } // Here is the city i've put
        
        let task = URLSession.shared.dataTask(with: URLRequest(url: url)) { data, _, error in
            guard let data = data, error == nil else {
                return
            }
            do {
                let results = try JSONDecoder().decode(MainWeatherDataModel.self, from: data)
                completion(.success(results.results))
            } catch {
                completion(.failure(error))
            }
        }
        task.resume()
    }
}

My project contains CollectionView inside TableView. Parsed data filling Cells But it's only one city showing in App. I need 19 more. So my questions are: How can I implement different queries in URL or Is there a method do to multiple parsing?

Thank you

CodePudding user response:

Here is a very basic example code, to fetch the weather for a number of cities using your modified setup. It shows how to implement different queries using the URL, as per the question.

Note, you should read about (and use) Swift async/await concurrency, to fetch all the data concurrently.

struct Constants {
    static let API_KEY = "api-key"
    static let baseURL = "https://api.openweathermap.org/data/2.5/weather?appid=\(API_KEY)&units=metric&q="
}

class APICaller {
    
    static let shared = APICaller()
    
    // -- here
    func getData(cityName: String, completion: @escaping(Result<[WeatherDataModel], Error>) -> Void) {
        // -- here
        guard let url = URL(string: (Constants.baseURL   cityName)) else { return } 
        
        URLSession.shared.dataTask(with: URLRequest(url: url)) { data, _, error in
            guard let data = data, error == nil else { return }
            do {
                let results = try JSONDecoder().decode(MainWeatherDataModel.self, from: data)
                // -- here
                if let weather = results.weather {
                    completion(.success(weather))
                } else {
                    completion(.success([]))
                }
            } catch {
                completion(.failure(error))
            }
        }.resume()
    }
}

struct ContentView: View {
    @State var citiesWeather: [String : [WeatherDataModel]] = [String : [WeatherDataModel]]()
    @State var cities = ["London", "Tokyo", "Sydney"]
    
    var body: some View {
        List(cities, id: \.self) { city in
            VStack {
                Text(city).foregroundColor(.blue)
                Text(citiesWeather[city]?.first?.description ?? "no data")
            }
        }
        .onAppear {
            for city in cities {
                fetchWeatherFor(city) // <-- no concurrency, not good
            }
        }
    }
    
    func fetchWeatherFor(_ name: String) {
        APICaller.shared.getData(cityName: name) { result in
            switch result {
                case .success(let arr): citiesWeather[name] = arr
                case .failure(let error): print(error) // <-- todo
            }
        }
    }
}

struct WeatherDataModel: Identifiable, Decodable {
    public let id: Int
    public let main, description, icon: String
}

struct MainWeatherDataModel: Identifiable, Decodable {
    let id: Int
    let weather: [WeatherDataModel]?
}
  • Related