[iOS] OpenWeather API 사용하기 - JSON Parsing

LeeEunJae·2023년 3월 22일
0

https://www.udemy.com/course/ios-13-app-development-bootcamp/
해당 강의를 들으면서 배운 내용을 토대로 정리한 글 입니다.
전체 코드를 다루기 보다는 Json Parsing 관련 코드를 중점적으로 다룹니다.

📌 Open Weather API

https://openweathermap.org/

이번 예제에서 사용할 Open API 입니다.
도시 이름 혹은 위도와 경도를 통해 해당 도시의 날씨 데이터를 가져올 수 있습니다.

다음과 같이 http 요청을 하면, json 형태로 날씨 데이터를 보여줍니다.

https://api.openweathermap.org/data/2.5/weather?appid=b609537845bae946211b5ecc679076e5&units=metric&q=London

https://api.openweathermap.org/data/2.5/weather?appid=b609537845bae946211b5ecc679076e5&units=metric&lat=37.541&lon=126.986

JSON Viewer Pro 라는 크롬 확장 앱을 다운로드 받으시면 위와 같이 예쁘게 json 을 보여줍니다.
아래 링크를 통해 다운로드 받으시면 됩니다.
https://chrome.google.com/webstore/detail/json-viewer-pro/eifflpmocdbdmepbjaopkkhbfmdgijcc?hl=ko

📌 JSON 객체 변환을 위한 구조체 생성

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 프로토콜을 통해 가져오는 작업을 해보겠습니다.

📌 URLSession, JSONDecoder...

	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 를 통해 접근할 수 있겠죠

끝 🙇‍♂️

profile
매일 조금씩이라도 성장하자

0개의 댓글