To practice URLSession to fetch json data from open weather API
⛅️ weatherApp-iOS-practice
? 기능 상세
-
도시 이름을 입력하면 현재 날씨 정보를 가져와 화면에 표시되게 만들어야 합니다
-
도시 이름을 잘못 입력하면 서버로부터 응답받은 에러 메시지가 alert으로 표시 됩니다
? Check Point !
? Current Weather API (OpenWeather API)
json 과 struct 구조체(model) mapping 하기
// WeatherInfo.swift
import Foundation
// Codable 은 자신을 변환하거나, 외부표현으로 변환 할 수 있는 (예, .json) 타입을 의미함
// Codable 은 decodable(자신을 외부에 decoding 타입), encodable(자신을 외부에서 encoding 타입)
// Codable protocol 을 채택 했다는 것은 Json decoding, encoding 이 모두 가능 하다는 것임, 즉 Json <-> WeatherInfo 객체
struct WeatherInfo: Codable {
let weather: [Weather]
let temp: Temp
let nameL: String
enum CodingKeys: String, CodingKey {
case weather
case temp = "main"
case name
}
}
struct Weather: Codable {
let id: Int
let main: String
let description: String
let icon: String
}
// 만약 json의 property 이름과 type 의 이름이 다를 경우 type 내부에서 codingKeys 라는 String type 의 열거형을 선언하고 codingKey protocol 을 준수하게 만들어야 함
// main property 의 json 에서 temp struct 에 mapping 시키기 위해서 property 정의함
struct Temp: Codable {
let temp: Double
let feelsLike: Double
let minTemp: Double
let maxTemp: Double
enum CodingKeys: String, CodingKey {
case temp
case feelsLike = "feels_like"
case minTemp = "temp_min"
case maxTemp = "temp_max"
}
}
response 한 data를 UI 에 업데이트
//
// UI창에 weatherInfo 가 나타나게 하는 method
func configureView(weatherInfo: WeatherInfo) {
self.cityNameLabel.text = weatherInfo.name
// weatherInfor 안에 wather 의 첫번째 상수에 대입
if let weather = weatherInfo.weather.first {
self.weatherDescriptionLabel.text = weather.description
}
self.tempLabel.text = "\(Int(weatherInfo.temp.temp))°C"
self.minTempLabel.text = "최저: \(Int(weatherInfo.temp.minTemp))°C"
self.maxTempLabel.text = "최고: \(Int(weatherInfo.temp.maxTemp))°C"
}
- 도시의 현재 날씨 정보를 가져옵니다
? URLSession
func getCurrentWeather(cityName: String) {
guard let url = URL(string: "https://api.openweathermap.org/data/2.5/weather?q=\(cityName)&units=metric&lang=kr&appid=0fb8463dce1de96897cba0b1eff08e18") else { return }
// session 을 default session 으로 설정
let session = URLSession(configuration: .default)
// compression handler 로써 closure 매개 변수에 data(서버에서 응답 받은 data), response(HTTP header 나 상태 코드의 metaData), error(error 코드 반환)
session.dataTask(with: url) { [weak self] data, response, error in
......
}
Describing check point in details in Jacob’s DevLog – https://jacobko.info/ios/ios-06/
❌ Error Check Point
? API Response Error 발생시 Error 처리
위와 같이 textField 에서 도시이름이 오타나 검색이 되지 않으면, 404 error 가 발생합니다. 그럴때 alert 창으로 도시이름이 일치하지 않습니다 라는 나오게 하는 code 는 다음과 같습니다
- Error message 처리를 위한 struct 모델 생성
// in ViewController.swift
// Error message 가 alert 에 표시되게 하는 logic
func showAlert(message: String) {
let alert = UIAlertController(title: "Error", message: message, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "확인", style: .default, handler: nil))
self.present(alert, animated: true, completion: nil)
}
// URLSession 을 이용해서 currentWeather API를 호출하기
func getCurrentWeather(cityName: String) {
guard let url = URL(string: "https://api.openweathermap.org/data/2.5/weather?q=\(cityName)&units=metric&lang=kr&appid=0fb8463dce1de96897cba0b1eff08e18") else { return }
// session 을 default session 으로 설정
let session = URLSession(configuration: .default)
// compression handler 로써 closure 매개 변수에 data(서버에서 응답 받은 data), response(HTTP header 나 상태 코드의 metaData), error(error 코드 반환)
session.dataTask(with: url) { [weak self] data, response, error in
// 응답받은 response (json data)를 weatherInfo struct 에 decoding 되게 하는 logic
let successRange = (200..<300)
guard let data = data, error == nil else { return }
let decorder = JSONDecoder()
// 응답받은 data 의 statusCode 가 200번대 (200 ~ 299) 일때
if let response = response as? HTTPURLResponse, successRange.contains(response.statusCode) {
guard let weatherInfo = try? decorder.decode(WeatherInfo.self, from: data) else { return }
// debugPrint(weatherInfo)
// 받아온 데이터를 UI 에 표시하기 위해서는 main thread 에서 작업을 진행 햐여 됩
DispatchQueue.main.async {
self?.weatherStackView.isHidden = false
self?.configureView(weatherInfo: weatherInfo)
}
} else { // status code 가 200 번대가 아니면 error 상태 이니까 error message 생성 logic
guard let errorMessage = try? decorder.decode(ErrorMessage.self, from: data) else { return }
// debugPrint(errorMessage)
// main thread 에서 alert 이 표시되게 해야됨
DispatchQueue.main.async {
self?.showAlert(message: errorMessage.message)
}
}
}.resume() // app 이 실행되게 함
}
? ? ? ? ?
? Reference
Jacob’s DevLog – https://jacobko.info/ios/ios-08/
아직은 어렵지 – https://greatpapa.tistory.com/66
fastcampus – https://fastcampus.co.kr/dev_online_iosappfinal