0

I am calling the API service from my viewController using a private function.

private func updateCells() -> Void {
    let cities: [String] = ["New York", "London", "Tokyo", "Toronto", "Sydney", "Paris"]
    
    for cityName in cities {
        print(cityName)
        queryService.getSearchResults(cityName: cityName){results, errorMessage in
            
            if let results = results{
                self.myCity = results
                self.CityGrid.reloadData()
                self.list.append(results)
                print("Test -> name: \(results.name)")
                print("Test -> description: \(results.description)")
                print("Test -> currentTemp: \(Int(results.currentTemperature))")
                print(self.list[0])
                
            }
            
            if !errorMessage.isEmpty {
              print("Search error: " + errorMessage)
                let alert = UIAlertController(title: "Error", message: errorMessage, preferredStyle: UIAlertController.Style.alert)
                alert.addAction(UIAlertAction(title: "OK", style: UIAlertAction.Style.cancel, handler: nil))
                self.present(alert, animated: true, completion: nil)
            }
        }
    }
}

Here the QueryService class function to handle the API calls.

func getSearchResults(cityName: String, completion: @escaping QueryResult) {
    
    dataTask?.cancel()
    
    let url = URL(string: "http://api.openweathermap.org/data/2.5/weather?q=\(cityName)&units=metric&appid=zezfac1ecbe0511f1ac192add4ff112e")!
  
    
    dataTask = defaultSession.dataTask(with: url) { [weak self] data, response, error in
        
        
        defer {
            self?.dataTask = nil
        }
        
        if let error = error {
            self?.errorMessage += "Default Task Error: " + error.localizedDescription + "\n"
        } else if
            let data = data,
            let response = response as? HTTPURLResponse,
            response.statusCode == 200 {
            
            self?.updateRestults(data)
            
            DispatchQueue.main.async {
                completion(self?.location, self?.errorMessage ?? "")
            }
        }
        else if let res = response as? HTTPURLResponse,
                res.statusCode == 404{
            self?.errorMessage = "City Not Found"
            DispatchQueue.main.async {
                
                completion(self?.location, self?.errorMessage ?? "Not Found")
            }
            
            //print("City Not found")
        }
    }
    dataTask?.resume()
}

error message picture

But I get the error Unexpectedly found nil while unwrapping an Optional value. But if I use a hard-coded string it works fine. What am I doing wrong here? Since the cities array is of type String,

Palwandew
  • 70
  • 1
  • 7
  • 1
    Can you also mention the line of crash? – Tushar Sharma May 25 '21 at 19:56
  • let url = URL(string: "http://api.openweathermap.org/data/2.5/weather?q=\(cityName)&units=metric&appid=zezfac1ecbe0511f1ac192add4ff112e")! – Palwandew May 25 '21 at 20:13
  • You'll probably need to encode the URL first: https://stackoverflow.com/q/24551816/14351818 – aheze May 25 '21 at 20:24
  • You said hardcoded value works, can you show the difference in code for both cases? Currently what you have in above code for URL, is it hardcoded? – Tushar Sharma May 25 '21 at 20:24
  • Case - 1: Hardcoded value this works fine. queryService.getSearchResults(cityName: "London"){results, errorMessage in Case - 2 queryService.getSearchResults(cityName: cityName){results, errorMessage in – Palwandew May 25 '21 at 20:31

1 Answers1

1

The issue here is that the string "New York" which has spaces in it, and those spaces are put directly into the URL, which causes the initialization of the URL to fail. Spaces are not valid in URLs without first encoding them to %20.

You should change the line let cities: [String] = ... to var cities: [String] = ... and add this right line underneath it:

cities = cities.map { $0.replacingOccurrences(of: " ", with: "%20") }

This will replace every space in the city names with a %20 and store it back into the variable cities.

AlephNil
  • 56
  • 1
  • 5
  • Getting the same error after adding the lines you mentioned as well as tried the addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) method as well – Palwandew May 25 '21 at 22:04
  • I'm sorry, but I can't seem to replicate the error with that line added. Is the function called anywhere else in the program without encoding any invalid characters? – AlephNil May 26 '21 at 02:22
  • I did some research and found out that the OpenWeatherMap API uses the '+' sign for the blank spaces. So the replacingOccurances(of: " ", with: "+") method worked for me. – Palwandew May 26 '21 at 15:39