오늘은 강의영상을 마무리 하고 개인 과제를 시작했다. 늘 하던대로 한번 요구사항에 맞춰서 일단 동작하도록 빠르게 한번 만들어본 후 리팩토링을 거치며 완성할 것이다.
Alamofire를 사용하지 않고 구현한다.
기본적인 fetchData 메서드를 생성한다. 이후 fetchData를 이용하는 fetchCurrentWeatherResult와 fetchForecastWeatherResult를 구현한다.
fetchData는 통신을 하기 위한 설정이라고 생각하면 된다. 이 경로를 통해 데이터를 받아오고 디코딩하는 과정까지를 정의한다.
extension ViewController {
private func fetchData<T: Decodable>(url: URL, completion: @escaping(T?) -> Void) {
let session = URLSession(configuration: .default)
session.dataTask(with: url) { data, response, error in
guard let data, error == nil else {
print("데이터 로드 실패")
completion(nil)
return
}
let successRange: Range = (200..<300)
if let response = response as? HTTPURLResponse, successRange.contains(response.statusCode) {
guard let decodedData = try? JSONDecoder().decode(T.self, from: data) else {
print("JSON 디코딩 실패")
completion(nil)
return
}
completion(decodedData)
} else {
print("응답 오류")
completion(nil)
}
}.resume()
}
}
url을 입력하고 해당 url을 이용하여 fetchData를 진행하여 받아온 데이터를 실제 UI에 사용할 수 있도록 정의한다.
private func fetchCurrentWeatherResult() {
var urlComponents = URLComponents(string: "https://api.openweathermap.org/data/2.5/weather")
urlComponents?.queryItems = self.urlQueryItems
guard let url = urlComponents?.url else {
print("잘못된 url")
return
}
fetchData(url: url) { [weak self] (result: CurrentWeatherResult?) in
guard let self, let result else { return }
DispatchQueue.main.async {
self.tempLabel.text = "\(Int(result.main.temp))°C"
self.tempMinLabel.text = "최저: \(Int(result.main.tempMin)) °C"
self.tempMaxLabel.text = "최고: \(Int(result.main.tempMax)) °C"
}
guard let imageUrl = URL(string: "https://openweathermap.org/payload/api/media/file/\(result.weather[0].icon)@2x.png") else { return }
if let data = try? Data(contentsOf: imageUrl), let image = UIImage(data: data) {
self.imageView.image = image
}
}
}
위에서 구현한 fetchData를 Alamofire를 이용하여 구현하면 아래와 같다.
extension ViewController {
private func fetchDataByAlamofire<T: Decodable>(url: URL, completion: @escaping(Result<T, AFError>) -> Void) {
AF.request(url).responseDecodable(of: T.self) { response in
completion(response.result)
}
}
}
또한 fetchCurrentWeatherResult를 Alamofire로 구현하면 아래와 같다.
private func fetchCurrentWeatherResultbyAlamofire() {
var urlComponents = URLComponents(string: "https://api.openweathermap.org/data/2.5/weather")
urlComponents?.queryItems = self.urlQueryItems
guard let url = urlComponents?.url else {
print("잘못된 url")
return
}
fetchDataByAlamofire(url: url) { [weak self] (result: (Result<CurrentWeatherResult, AFError>)) in
guard let self else { return }
switch result {
case .success(let result):
DispatchQueue.main.async {
self.tempLabel.text = "\(Int(result.main.temp))°C"
self.tempMinLabel.text = "최저: \(Int(result.main.tempMin)) °C"
self.tempMaxLabel.text = "최고: \(Int(result.main.tempMax)) °C"
}
guard let imageUrl = URL(string: "https://openweathermap.org/payload/api/media/file/\(result.weather[0].icon)@2x.png") else { return }
AF.request(imageUrl).responseData { response in
if let data = response.data, let image = UIImage(data: data) {
self.imageView.image = image
}
}
case .failure(let error):
print("에러 \(error)")
}
}
}