VesselWheel

5일치 3시간 단위 날씨예보 배열에 넣기(with OpenWeatherMap) (1/2) 본문

Xcode Study

5일치 3시간 단위 날씨예보 배열에 넣기(with OpenWeatherMap) (1/2)

JasonYang 2024. 2. 13. 16:43

https://vesselwheel.tistory.com/185

 

3시간 단위 5일치 OpenWeatherMap API를 활용한 데이터 호출

기존에 WeatherData를 활용한 현재 날씨 API는 위경도 혹은 도시지역의 현재 날씨 기준으로 실시간 기상예보이다. // MARK: - public Methods //현재 날씨정보 : LocationManager에서 위치정보를 받고, 위경도 API

vesselwheel.tistory.com


상기 주소의 이전글을 참고하여

//위치데이터

1. LocationMager 클래스에서 싱글톤으로 현재위치 혹은 지정위치를 호출하여 위경도를 받으면, 

 

// API call

2. WeatherManager의 getForestWeather 매소드가 파라미터로 받은 위경도 기준 지역의, API에서 날씨 데이터를 호출(API call)한다.

 

이어서,

API call에서 받은 데이터를 forecastData의 빈배열에 넣어, 뷰계층에서 사용할 수 있도록 데이터를 가공해야한다.  

-> ViewController에서 데이터를 가공하게되면, 코드단에서 길어지고, 복잡해지기 때문에 네트워크요청하는 WeatherManager에서 싱글톤 패턴의 하위에 있는 매소드에 넣었다. 

// API call에서 데이터를 forecastData 배열에 넣기 

3. API call에서 url이 정상적으로 주입되면 let weatherData에 data가 저장이 된다. 

더보기
    public func getForecastWeather(latitude: Double, longitude: Double, completion: @escaping(Result<[(time: String, weatherIcon: String, temperature: Int, wind: String)], NetworkError>) -> Void) {
        // 위치데이터
        LocationManager.shared.setLocation(latitude: latitude, longitude: longitude)
        guard let currentLocation = LocationManager.shared.currentLocation else {
            return completion(.failure(.badLocation))
        }
        
        //API call
        guard let url = URL.urlForForecastForLocation(currentLocation, apiKey: apiKey) else {
            return completion(.failure(.badUrl))
        }
        
        //API call에서 데이터를 forecastData 배열에 넣기
        performRequestForecast(with: url) { result in
            switch result {
            case .success(let weatherData):
                // 날씨 데이터를 성공적으로 받아왔을 때
                var forecastData: [(time: String, weatherIcon: String, temperature: Int, wind: String)] = []
                
                for list in weatherData.list {
                    let time = list.dtTxt
                    let weatherIcon = list.weather.first?.icon ?? ""
                    let temperature = Int(list.main.temp)
                    let windSpeed = list.wind.speed
                    
                    let forecast = (time: time, weatherIcon: weatherIcon, temperature: temperature, wind: "\(windSpeed) m/s")
                    forecastData.append(forecast)
                }
                
                completion(.success(forecastData))
                
            case .failure(let error):
                // 날씨 데이터를 받아오는데 실패했을 때
                completion(.failure(error))
            }
        }
    }

3-1. getForestWeather 매소드에서 API call을 요청하면, 종속된 private performRequestForecast 매소드를 활용하여 주입된 데이터를 디코딩해준다. 

해석 

더보기
  • performRequestForecast(with:completion:) 메소드는 URL을 인자로 받아, 이를 통해 HTTP 요청을 수행합니다. 이 메소드는 비동기적으로 동작하며, 요청이 완료되면 클로저를 통해 결과를 반환합니다.

 

  • URLSession.shared.dataTask(with:completion:)를 사용하여 실제 HTTP 요청을 수행합니다. 이 함수는 비동기적으로 동작하며, 서버로부터 응답이 오면 클로저를 호출합니다.

 

  • 클로저 내부에서는 먼저 에러가 있는지 확인합니다. 에러가 있거나 데이터가 없는 경우, .failure(.noData)를 반환해 에러를 알립니다.

 

  • 그 다음으로, JSONDecoder를 사용해 서버로부터 받은 데이터를 디코딩합니다. 디코딩된 데이터는 WeatherData 타입의 인스턴스로 변환되며, 이는 .success(weatherData)를 통해 반환됩니다.

 

  • 만약 디코딩 과정에서 에러가 발생하면, 에러 메시지를 출력하고 .failure(.decodingError)를 통해 에러를 반환합니다.

 

  • 마지막으로, resume() 메소드를 호출하여 URLSession의 작업을 시작합니다. 이 메소드를 호출하지 않으면 네트워크 요청이 시작되지 않습니다.

이 코드는 Swift의 Result 타입을 사용하고 있습니다. Result 타입은 성공 또는 실패 두 가지 경우 중 하나를 나타내는 열거형으로, 성공한 경우에는 연관값으로 성공 결과를, 실패한 경우에는 연관값으로 에러를 가집니다. 이를 통해 비동기 작업의 결과를 표현하는 데 유용합니다.

 

더보기
    // OpenWeatherMap의 API 요청시 5일치 3시간 단위 예보 날씨정보를 처리하는 메소드
    private func performRequestForecast(with url: URL?, completion: @escaping (Result<WeatherData, NetworkError>) -> Void) {
        guard let url = url else {
            return completion(.failure(.badUrl))
        }
        
        URLSession.shared.dataTask(with: url) { data, Response, error in
            // 응답과 에러,데이터 출력 : 정상출력됨
//            print("ForecastData: \(String(describing: data))")
//            print("ForecastResponse: \(String(describing: Response))")
//            print("ForecastError: \(String(describing: error))")
            
            guard let data = data, error == nil else {
                return completion(.failure(.noData))
            }
            
            do {
                let weatherData = try JSONDecoder().decode(WeatherData.self, from: data)
                completion(.success(weatherData))
            } catch {
                print("Forecast Decoding error: \(error)")
                completion(.failure(.decodingError))
            }
        }.resume()
    }

4. ViewController ViewDidLoad()에서 테스트 

- forecastData 빈 배열을 초기화하고, 싱글톤으로 WeatherManager의 getForecastWeather매소드를 통해 데이터를 호출한다.

override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .white
        setUI()
        setAddSubView()
        setLayout()

        let latitude = 37.4536
        let longitude = 126.7317
        
        var forecastData: [(time: String, weatherIcon: String, temperature: Int, wind: String)] = []

        WeatherManager.shared.getForecastWeather(latitude: latitude, longitude: longitude) { result in
            switch result {
            case .success(let data):
                forecastData = data
                
                // forecastData 배열에 데이터가 들어갔는지 확인
                for forecast in forecastData {
                    print("Time: \(forecast.time)")
                    print("Weather Icon: \(forecast.weatherIcon)")
                    print("Temperature: \(forecast.temperature)°C")
                    print("Wind Speed: \(forecast.wind)")
                    print("----------")
                }
                
            case .failure(let error):
                print("Error: \(error)")
            }
        }
        
        
        // 3시간 뒤의 최고 온도와 최저 온도 업데이트
//        updateForecastWeather()
        // 위치정보를 가져오는 시간이 걸리더라도 날씨정보를 우선 업데이트해서 UI를 변경하여 viewDidLoad()에서 즉시 반환
//        LocationManager.shared.requestLocation()
    }
    
}

 

5. 콘솔창에 출력된 모습

더보기
Time: 2024-02-13 09:00:00
Weather Icon: 02d
Temperature: 286°C
Wind Speed: 4.46 m/s
----------
Time: 2024-02-13 12:00:00
Weather Icon: 02n
Temperature: 284°C
Wind Speed: 3.73 m/s
----------
Time: 2024-02-13 15:00:00
Weather Icon: 01n
Temperature: 282°C
Wind Speed: 2.69 m/s
----------
Time: 2024-02-13 18:00:00
Weather Icon: 03n
Temperature: 280°C
Wind Speed: 2.43 m/s
----------
Time: 2024-02-13 21:00:00
Weather Icon: 04n
Temperature: 280°C
Wind Speed: 2.46 m/s
----------
Time: 2024-02-14 00:00:00
Weather Icon: 04d
Temperature: 281°C
Wind Speed: 2.6 m/s
----------
Time: 2024-02-14 03:00:00
Weather Icon: 04d
Temperature: 283°C
Wind Speed: 3.21 m/s
----------
Time: 2024-02-14 06:00:00
Weather Icon: 04d
Temperature: 283°C
Wind Speed: 2.12 m/s
----------
Time: 2024-02-14 09:00:00
Weather Icon: 04d
Temperature: 281°C
Wind Speed: 1.28 m/s
----------
Time: 2024-02-14 12:00:00
Weather Icon: 04n
Temperature: 280°C
Wind Speed: 1.43 m/s
----------
Time: 2024-02-14 15:00:00
Weather Icon: 04n
Temperature: 280°C
Wind Speed: 1.6 m/s
----------
Time: 2024-02-14 18:00:00
Weather Icon: 04n
Temperature: 280°C
Wind Speed: 0.99 m/s
----------
Time: 2024-02-14 21:00:00
Weather Icon: 04n
Temperature: 279°C
Wind Speed: 2.5 m/s
----------
Time: 2024-02-15 00:00:00
Weather Icon: 10d
Temperature: 279°C
Wind Speed: 4.51 m/s
----------
Time: 2024-02-15 03:00:00
Weather Icon: 10d
Temperature: 278°C
Wind Speed: 6.52 m/s
----------
Time: 2024-02-15 06:00:00
Weather Icon: 10d
Temperature: 278°C
Wind Speed: 5.25 m/s
----------
Time: 2024-02-15 09:00:00
Weather Icon: 04d
Temperature: 277°C
Wind Speed: 5.48 m/s
----------
Time: 2024-02-15 12:00:00
Weather Icon: 04n
Temperature: 276°C
Wind Speed: 5.03 m/s
----------
Time: 2024-02-15 15:00:00
Weather Icon: 01n
Temperature: 274°C
Wind Speed: 3.16 m/s
----------
Time: 2024-02-15 18:00:00
Weather Icon: 01n
Temperature: 274°C
Wind Speed: 1.92 m/s
----------
Time: 2024-02-15 21:00:00
Weather Icon: 02n
Temperature: 274°C
Wind Speed: 1.19 m/s
----------
Time: 2024-02-16 00:00:00
Weather Icon: 02d
Temperature: 274°C
Wind Speed: 0.17 m/s
----------
Time: 2024-02-16 03:00:00
Weather Icon: 01d
Temperature: 277°C
Wind Speed: 2.02 m/s
----------
Time: 2024-02-16 06:00:00
Weather Icon: 01d
Temperature: 277°C
Wind Speed: 3.22 m/s
----------
Time: 2024-02-16 09:00:00
Weather Icon: 01d
Temperature: 276°C
Wind Speed: 2.66 m/s
----------
Time: 2024-02-16 12:00:00
Weather Icon: 01n
Temperature: 275°C
Wind Speed: 1.64 m/s
----------
Time: 2024-02-16 15:00:00
Weather Icon: 01n
Temperature: 275°C
Wind Speed: 1.2 m/s
----------
Time: 2024-02-16 18:00:00
Weather Icon: 01n
Temperature: 274°C
Wind Speed: 1.43 m/s
----------
Time: 2024-02-16 21:00:00
Weather Icon: 01n
Temperature: 274°C
Wind Speed: 1.48 m/s
----------
Time: 2024-02-17 00:00:00
Weather Icon: 03d
Temperature: 274°C
Wind Speed: 1.46 m/s
----------
Time: 2024-02-17 03:00:00
Weather Icon: 01d
Temperature: 278°C
Wind Speed: 2.19 m/s
----------
Time: 2024-02-17 06:00:00
Weather Icon: 01d
Temperature: 279°C
Wind Speed: 2.48 m/s
----------
Time: 2024-02-17 09:00:00
Weather Icon: 01d
Temperature: 278°C
Wind Speed: 1.28 m/s
----------
Time: 2024-02-17 12:00:00
Weather Icon: 01n
Temperature: 278°C
Wind Speed: 0.94 m/s
----------
Time: 2024-02-17 15:00:00
Weather Icon: 01n
Temperature: 277°C
Wind Speed: 1.64 m/s
----------
Time: 2024-02-17 18:00:00
Weather Icon: 02n
Temperature: 277°C
Wind Speed: 1.87 m/s
----------
Time: 2024-02-17 21:00:00
Weather Icon: 01n
Temperature: 277°C
Wind Speed: 2.22 m/s
----------
Time: 2024-02-18 00:00:00
Weather Icon: 01d
Temperature: 278°C
Wind Speed: 2.23 m/s
----------
Time: 2024-02-18 03:00:00
Weather Icon: 01d
Temperature: 282°C
Wind Speed: 3.2 m/s
----------
Time: 2024-02-18 06:00:00
Weather Icon: 02d
Temperature: 285°C
Wind Speed: 3.15 m/s
----------