Weather

jusong23·2022년 5월 11일
0

Swift

목록 보기
6/7

Tric : URL로 OpenAPI가져오기(json), isHidden

기능 : 도시 이름을 TextField에 입력하면 가져오기/잘못 입력 시 Alert 메세지 표시

URLSession : HTTP / HTTPS 기반 요청을 처리하기 위한 클래스 및 클래스의 세트 모음

→ html문서를 주고 받는데 쓰이는 통신 Protocol

→ 특정 url을 이용하여 data를 다운로드 !


(1) TextField에 도시이름을 입력한다.

(2) 날씨가져오기 버튼을 눌러 API를 가져온다.

(3) API가 Fetch되면 WeatherInformation 배열에 담기고 이를 StoryBoard에 적용시킨다.

(4) 도시 이름 오 기입시, 해당 메세지는 ErrorMessage 배열에 담기고 이를 Alert로 표시한다.

📱Story Board

각 라벨들을 Stack View로 만들어 구분하기 - 전체 Stack View로 만들기

부모 Stack View Aligment : Center - spacing : 10

각 Child Stack View 속성 설정

부모 Stack View : 날씨 가져오기 이전엔 보여지면 안되므로 Hidden 속성으로 감추기

각 Label들 IBOulet 설정, 날씨가져오기 버튼은 IBAction.

🧑‍💻Code

  1. 구조체 따로 Swfit 파일로 만들어서 정의, 해당 구조체는 Codable을 채택(외부 json파일 같은거)
struct WeatherInformation: Codable {
    let weather: [Weather] // json파일에서 weather는 배열이므로 밑에 처럼 타입 선언을 한 것
    let temp: Temp
    let name: String
    
    enum CodingKeys: String, CodingKey {
        case weather
        case temp = "main"
        case name
    }
} // json 인코딩, 디코딩 가능 weather informaiton 객체 <-> json
// 날씨 정보 json 파일을 weather informaiton struct로 변환하는 작업(디코딩)

struct Weather: Codable {
    let id: Int
    let main: String
    let description: String
    let icon: String
}
// json 파일을 변환하고자 하면 , json파일의 key와 사용자가 정의한 struct의 타입이 일치해야!

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"
    }
    // json파일과 구조체에 선언된 이름이 달라도 Mappint될수 있도록, 
		// CodingKey 프로토콜을 채택하는 열거형 선언
}
  1. URLSession를 통해 OpenWeather API 가져오기
@IBAction func tapFetchWeatherButton(_ sender: UIButton) {
        if let cityName = self.cityNameTextField.text {
            self.getCurrentWeather(cityName: cityName)
            self.view.endEditing(true) // 버튼 눌리면 키보드 내려가는 기능
        }
    }
.
.
.
    func getCurrentWeather(cityName: String) {
        guard let url = URL(string: "https://api.openweathermap.org/data/2.5/weather?q=\(cityName)&appid=5448715390df41aed509eef3faa3053b") else { return }
        let session = URLSession(configuration: .default)
        session.dataTask(with: url) { data, response, error in
            guard let data = data, error == nil else { return } // 요청 성공 시 다음라인 가도록
            let decoder = JSONDecoder() // json 객체에서 data 유형의 인스턴스로 디코딩하는 객체! Decodable, Codable 프로토콜을 준수하는 라인!
            let weatherInformation = try? decoder.decode(WeatherInformation.self, from: data)
            // 1st Parameter : json을 매핑시켜줄 Codable Protocol을 준수하는 사용자 정의 타입(구조체 만들어 둔거)
            // 2nd Parameter : 서버에서 응답받은 json 데이터
            debugPrint(weatherInformation)
        }.resume() // dataTask.resume() 호출하여 작업 실행!
    }
}
  1. 올바른 도시이름으로 검색한 후 날씨 정보가 View에 표시 되도록
func configureView(weatherInformation: WeatherInformation) {
        self.cityNameLabel.text = weatherInformation.name
        if let weather = weatherInformation.weather.first {
            self.weatherDiscription.text = weather.description
        }
        self.tempLabel.text = "\(Int(weatherInformation.temp.temp - 273.15))°C"
        self.minTempLabel.text = "\(Int(weatherInformation.temp.minTemp - 273.15))°C"
        self.maxTempLabel.text = "\(Int(weatherInformation.temp.maxTemp - 273.15))°C"
    } // 함수안에서 쓰일 parameter weatherInformation는 앞에서 정의한 WeatherInformation 배열에 저장된 값들이다!
.
.
.
func getCurrentWeather(cityName: String) {
        guard let url = URL(string: "https://api.openweathermap.org/data/2.5/weather?q=\(cityName)&appid=5448715390df41aed509eef3faa3053b") else { return }
        let session = URLSession(configuration: .default)
        session.dataTask(with: url) { [weak self] data, response, error in
            guard let data = data, error == nil else { return } // 요청 성공 시 다음라인 가도록
            let decoder = JSONDecoder() // json 객체에서 data 유형의 인스턴스로 디코딩하는 객체! Decodable, Codable 프로토콜을 준수하는 라인!
            let weatherInformation = try? decoder.decode(WeatherInformation.self, from: data)
            // 1st Parameter : json을 매핑시켜줄 Codable Protocol을 준수하는 사용자 정의 타입(구조체 만들어 둔거)
            // 2nd Parameter : 서버에서 응답받은 json 데이터
						DispatchQueue.main.async {
                self?.weatherStackView.isHidden = false
                self?.configureView(weatherInformation: weatherInformation)
            } 
						// 네트워크 작업은 별도의 Thread에서 진행되고 응답이 온다해도 main Thread로 오지않기에
						// Main Thread로 오도록 설정!

        }.resume() // dataTask.resume() 호출하여 작업 실행!
    }
  1. Chrome HTTP Status Code(200 vs 400)을 통해 날씨 정보 오기입 시 에러를 Alert
struct ErrorMessage: Codable {
    let message: String
}
func getCurrentWeather(cityName: String) {
.
.
let successRange = (200..<300)
guard let data = data, error == nil else { return }
 // 요청 성공 시 다음라인 가도록
let decoder = JSONDecoder() 
// json 객체에서 data 유형의 인스턴스로 디코딩하는 객체! Decodable, Codable 프로토콜을 준수하는 라인!
  if let response = response as? HTTPURLResponse, successRange.contains(response.statusCode) {
      guard let weatherInformation = try? decoder.decode(WeatherInformation.self, from: data) else { return }
      // 1st Parameter : json을 매핑시켜줄 Codable Protocol을 준수하는 사용자 정의 타입(구조체 만들어 둔거)
      // 2nd Parameter : 서버에서 응답받은 json 데이터
      DispatchQueue.main.async {       // 메인 Thread에서 작업하도록
          self?.weatherStackView.isHidden = false
          self?.configureView(weatherInformation: weatherInformation)
      }
  } else {
      guard let errorMessage = try? decoder.decode(ErrorMessage.self, from: data) else { return }
      DispatchQueue.main.async {
          self?.showAlert(mesaage: errorMessage.message)
      }
			// 1st Parameter : json을 매핑시켜줄 Codable Protocol을 준수하는 사용자 정의 타입(구조체 만들어 둔거)
      // 2nd Parameter : 서버에서 응답받은 json 데이터
  }
}
func showAlert(mesaage: String){
        let alert = UIAlertController(title: "Error", message: mesaage, preferredStyle: .alert)
        alert.addAction(UIAlertAction(title: "확인", style: .default, handler: nil))
        self.present(alert, animated: true, completion: nil)
    }

URLSession : URLSessionConfiguration을 통해 생성가능

공유 세션 : URlSession.shared( )
기본 세션 : URlSession(configuration: .defalut)
임시 세션 : URlSession(configuration: .ephemeral)
백그라운드 세션 : URlSession(configuration: .background)

→ 이렇게 생성된 URLSession은 한개 이상의 URLSessionTask을 생성가능하며, 이를 통해 실제 서버와 통신할 수 있음

URLSessionDataTask : 짧고 빈번한 요청
URLSessionUploadTask
URLSessionDownloadTask
URLSessionStreamTask
URLSessionWebSocketTask

  1. Session Configuration 결정 후, Session을 생성
  2. 통신할 URL과 Request 객체를 결정
  3. 사용할 Task를 결정하고 그에 맞는 Completion Handle 클로저 (a function that calls back when a task completes { data, response, error } )나 Delegate 메소드들을 작성
  4. 해당 Task를 실행
  5. Task 완료 후 Completion Handler Closer{ data, response, error } 가 호출이 됨

0개의 댓글