https://www.udemy.com/course/ios-13-app-development-bootcamp/
해당 강의를 들으면서 배운 내용을 토대로 정리한 글 입니다.
전체 코드를 다루기 보다는 Json Parsing 관련 코드를 중점적으로 다룹니다.
이번 예제에서 사용할 Open API 입니다.
도시 이름 혹은 위도와 경도를 통해 해당 도시의 날씨 데이터를 가져올 수 있습니다.
다음과 같이 http 요청을 하면, json 형태로 날씨 데이터를 보여줍니다.
JSON Viewer Pro 라는 크롬 확장 앱을 다운로드 받으시면 위와 같이 예쁘게 json 을 보여줍니다.
아래 링크를 통해 다운로드 받으시면 됩니다.
https://chrome.google.com/webstore/detail/json-viewer-pro/eifflpmocdbdmepbjaopkkhbfmdgijcc?hl=ko
json 데이터를 모두 가져오기 보다는 필요한 데이터만 가져오도록 하겠습니다.
필요한 데이터는 name, main.temp, weather[0].id 입니다.
Json Parsing을 하기 위해서 아래와 같이 구조체를 만듭니다.
struct WeatherData: Codable {
let name: String
let main: Main
let weather: [Weather]
}
struct Main: Codable {
let temp: Double
}
struct Weather: Codable {
let id: Int
}
Codable 은 무엇일까요?
Apple Developer 에서는 다음과 같이 Codable 을 소개합니다.
A type that can convert itself into and out of an external representation.
이렇게만 봐서는 무슨 소리인지 모르겠어서 구글링을 해서 알아봤습니다.
결론부터 말하자면, Decodable 과 Encodable 이라는 프로토콜을 둘 다 사용할 수 있는 프로토콜 타입이라고 합니다. JSON 객체로 또는, JSON 객체로부터 변환하고 싶은 형태의 구조체를 Codable 프로토콜을 사용해서 만들고, JSONDecoder를 사용해서 디코딩할 수 있습니다.
이제 실제 날씨 데이터를 http 프로토콜을 통해 가져오는 작업을 해보겠습니다.
let weatherURL = "https://api.openweathermap.org/data/2.5/weather?appid=b609537845bae946211b5ecc679076e5&units=metric"
func fetchWeather(cityName: String) {
let urlString = "\(weatherURL)&q=\(cityName)"
performRequest(with: urlString)
}
func fetchWeather(latitude: CLLocationDegrees, longitude: CLLocationDegrees) {
let urlString = "\(weatherURL)&lat=\(latitude)&lon=\(longitude)"
performRequest(with: urlString)
}
private func performRequest(with urlString: String) {
if let url = URL(string: urlString) {
let session = URLSession(configuration: .default)
let task = session.dataTask(with: url) { data, response, error in
if error != nil {
self.delegate?.didFailWithError(error: error!)
return
}
if let safeData = data {
if let weather = self.parseJSON(safeData) {
self.delegate?.didUpdateWeather(self, weather: weather)
}
}
}
task.resume()
}
}
기본적으로 사용하는 url 을 weatherURL 에 저장합니다.
fetchWeather 함수는 도시 이름을 통해 날씨 데이터를 가져올 수도 있고, 위도와 경도로 가져올 수 있습니다.
performRequest 는 파라미터로 fetchWeather 를 통해 생성한 url 통해 Request 를 합니다.
통신을 위해 URLSession 을 사용했습니다.
URLSession 을 생성하고, url을 통해 Task 를 생성합니다.
Task 가 완료되어서 데이터를 가져왔거나 통신 오류가 발생한 경우를 처리하기 위해 Closure 를 사용해서 CompletionHandler 를 정의해줍니다.
이 때, data, response, error 는 모두 옵셔널입니다.
error 가 발생한 경우와 데이터를 성공적으로 가져온 경우 모두 Delegate Pattern 을 사용해서 error 를 처리하거나 UI를 업데이트하는 작업을 처리했습니다.
데이터를 성공적으로 가져온 경우 JSON 형태의 데이터를 디코딩한 후 UI 를 업데이트 합니다.
func parseJSON(_ weatherData: Data) -> WeatherModel? {
let decoder = JSONDecoder()
do{
let decodedData = try decoder.decode(WeatherData.self, from: weatherData)
let id = decodedData.weather[0].id
let temp = decodedData.main.temp
let name = decodedData.name
let weather = WeatherModel(conditionId: id, cityName: name, temperature: temp)
return weather
} catch {
delegate?.didFailWithError(error: error)
return nil
}
}
parseJSON 함수는 파라미터로 위에서 통신 결과로 받아온 JSON 형식의 데이터를 받습니다.
리턴은 WeatherModel 타입을 리턴하는데, UI 를 업데이트 하기 위한 데이터들을 Struct 로 만든 것 입니다. 중요한 부분은 아니기 때문에 생략합니다.
위에서 언급한 JSONDecoder 를 사용해서 JSON 데이터를 디코딩합니다.
디코딩 실패시 Error 를 Throw 하므로 try-catch 문을 사용합니다.
디코딩 성공시 decodedData는 위에서 만든 WeatherData 구조체 타입 입니다.
구조체이므로 날씨 데이터에 대한 접근을 dot syntax 를 통해 접근할 수 있겠죠