-
날씨 앱iOS/패스트캠퍼스(앱제작) 2022. 5. 8. 13:43
<주요 기능>
1. 도시 이름 입력 -> 현재 날씨 정보를 가져와 화면에 표시
2. 도시이름 잘못 입력 -> 서버로부터 응답받은 에러 메시지를 alert으로 표시
- Codable
- Current Weather API
- URLSession
- URLSession을 이용한 HTTP통신
URLSession
- 특정 url을 이용하여 데이터를 다운로드 하고 업로드 하기 위한 API (앱에서 서버와 통신하기 위해 제공하는 API)
- 탭 또는 창 당 하나의 URLSession을 생성 (URLSessionConfiguration을 통해 생성)
-> 특정 url에 대한 요청을 나타내는 일련의 작업 추가
- request, response 가 기본 구조
- request: 서버로 요청 보낼때 어떤 http메소드를 사용할 것인지, 캐싱 정책을 어떻게 할것인지 등을 설정
- response: url요청에 응답
URLSession의 종류
1) 공유세션 (Shared Session) / URLSession.shared()
- 싱글톤으로 사용 가능
- 기본 요청을 하기 위한 세션. 맞춤설정은 불가 하지만 쉽게 만들어 사용 가능
2) 기본 세션 (Default Session ) / URLSession(configuration: .default)
- 직접 원하는 설정 가능. 캐쉬, 쿠키 인증 등을 디스크에 저장
- 순자척으로 데이터를 처리하기 위해 delegate 지정 가능
3) 임시 세션 (Ephemeral Session) / URLSession(configuration: .ephemeral)
- 캐쉬, 쿠키, 사용자 인증 정보를 디스크에 저장하지 않고 메모리에 올려서 세션을 연결
- 세션 만료시 데이터 사라짐
4) 백그라운드 세션 (Background Session) / URLSession(configuration: .background)
- 앱이 실행되지 않는 동안 백그라운드 에서 컨텐츠 업로드, 다운로드 수행 가능
URLSession이 구성되면 URLSessionTask를 이용해서 각 세션 내 작업 추가
URLSessionTask의 종류
1) URLSessiodnDataTask
- 데이터객체를 사용하여 데이터를 요청, 응답
- 짧고 빈번하게 요청되는 경우 사용
2) URLSessionUploadTask
- 데이터객체 or 파일 형태의 데이터를 업로드 하는 작업 수행
- 앱이 실행되지 않았을 때 백그라운드 업로드 지원
3) URLSessionDownloadTask
- 데이터를 다운받아서 파일 형태로 저장하는 작업 수행
- 앱이 실행되지 않았을 때 백그라운드 다운로드 지원
4) URLSessionStreamTask
- TCP/IP 연결을 생성할때 사용
5) URLSessionWebSocketTask
- WebSocket 프로토콜을 통해 통신
URLSession Life Cycle
1. Session Configuration을 결정하고 Session 을 생성
2. 통신할 URL와 Request 객체 설정
3. 사용할 Task를 결정하고 그에 맞는 Completion Handler나 Delegate 메소드 작성
4. 해당 Task 실행
5. Task 완료 후 Completion Handler 클로저 호출됨
1. 현재 날씨정보 json데이터를 담을 구조체 선언
- Codable: 자신을 json과 같은 외부표현으로 변환할수 있는 타입 (encoding, decoding 모두 가능)
- WeatherInformation객체를 json 형태로 인코딩 or json객체를 WeatherInformation형태로 디코딩
- 서버에서 전달받은 날씨정보 json데이터를 WeatherInformation struct 타입으로 변환 (디코딩) 할 것임
더보기// 현재 날씨를 저장하는 구조체 import Foundation struct WeatherInformation: Codable { let weather: [Weather] let temp: Temp // json데이터의 main키와 매핑되어야 함 let name: String // 도시 이름 가져오기 enum CodingKeys: String, CodingKey { case weather case temp = "main" case name } } // 현재 날씨를 담고있는 weather key에서 필요한 프로퍼티만 구조체에 정의 struct Weather: Codable { let id: Int let main: String let description: String let icon: String } // 온도(Temp) 정보를 담고있는 main key에서 필요한 프로퍼티만 구조체에 정의 struct Temp: Codable { let temp: Double let feelsLike: Double let minTemp: Double let maxTemp: Double // 구조체에 선언된 프로퍼티와 서버에서 내려주는 json데이터의 키가 다를 경우 CodingKey 프로토콜을 통해 mapping enum CodingKeys: String, CodingKey { case temp case feelsLike = "feels_like" case minTemp = "temp_min" case maxTemp = "temp_max" } }
2. URLSession을 이용해서 current weather api 호출
더보기// ViewController.swift import UIKit class ViewController: UIViewController { @IBOutlet weak var cityNameTextField: UITextField! @IBOutlet weak var cityNameLabel: UILabel! @IBOutlet weak var weatherDescription: UILabel! @IBOutlet weak var maxTempLabel: UILabel! @IBOutlet weak var minTempLabel: UILabel! @IBOutlet weak var tempLabel: UILabel! override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view. } @IBAction func tapFetchWeahterBtn(_ 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=c942f7d5ac33681b9f170227d606626b") else { return } let session = URLSession(configuration: .default) // 서버로 데이터 요청, 응답 받기 // dataTask가 api를 호출 -> 서버에서 응답이 오면 completionHandler가 호출됨 // data:서버에서 응답받은 json데이터, response:http헤더 및 상태 코드와 같은 응답데이터, error: 요청에 실패하면 error객체/요청에 성공하면 nil반환 session.dataTask(with: url, completionHandler: { data, response, error in guard let data = data, error == nil else { return } let decoder = JSONDecoder() // json객체에서 데이터 유형의 사용자정의 타입의 인스턴스로 변환 // type: json데이터를 매핑시킬 codable프로토콜을 준수하는 사용자 정의 타입 / from: 서버에서 응답받은 json데이터 // 디코딩 실패 시 에러를 던지므로 try? let WeatherInformation = try? decoder.decode(WeatherInformation.self, from: data) debugPrint(WeatherInformation) }).resume() } }
3. 응답받은 json데이터를 뷰에 표시
- DispatchQue.main.sync
더보기// 날씨정보를 나타낼 stackView를 아울렛변수로 추가 --> isHidden속성 컨트롤 @IBOutlet weak var weaterStackView: UIStackView! // 메소드 생성 func configureView(weatherInformation: WeatherInformation) { self.cityNameLabel.text = weatherInformation.name if let weather = weatherInformation.weather.first { // weather배열의 첫번째요소 대입 self.weatherDescription.text = weather.description } self.tempLabel.text = "\(Int(weatherInformation.temp.temp - 273.15))" self.minTempLabel.text = "최저: \(Int(weatherInformation.temp.minTemp - 273.15))도" self.maxTempLabel.text = "최고: \(Int(weatherInformation.temp.maxTemp - 273.15))도" } func getCurrentWeather(cityName: String) { guard let url = URL(string: "https://api.openweathermap.org/data/2.5/weather?q=\(cityName)&appid=c942f7d5ac33681b9f170227d606626b") else { return } let session = URLSession(configuration: .default) // 서버로 데이터 요청, 응답 받기 // dataTask가 api를 호출 -> 서버에서 응답이 오면 completionHandler가 호출됨 // data:서버에서 응답받은 json데이터, response:http헤더 및 상태 코드와 같은 응답데이터, error: 요청에 실패하면 error객체/요청에 성공하면 nil반환 session.dataTask(with: url, completionHandler: { [weak self] data, response, error in guard let data = data, error == nil else { return } let decoder = JSONDecoder() // json객체에서 데이터 유형의 사용자정의 타입의 인스턴스로 변환 // type: json데이터를 매핑시킬 codable프로토콜을 준수하는 사용자 정의 타입 / from: 서버에서 응답받은 json데이터 // 디코딩 실패 시 에러를 던지므로 try? guard let WeatherInformation = try? decoder.decode(WeatherInformation.self, from: data) else { return } // WeatherInformation객체가 생성되면 configureView매개변수로 던짐 // 이때, 네트워크 작업은 별도의 스레드에서 진행됨. 응답이 와도 자동으로 메인스레드로 돌아오지 않음 // 따라서 completionHandler의 클로저 내에서 UI작업을 한다면, 메인스레드에서 작업이 되도록 해주어야함 DispatchQueue.main.async { self?.weaterStackView.isHidden = false self?.configureView(weatherInformation: WeatherInformation) } }).resume() }
4. 잘못된 도시이름을 입력했을 때 alert창 표시
- 에러데이터를 담을 수 있는 구조체 생성
- 통신 실패시 alert창 띄우는 로직 추가
더보기// ErrorMessage.swift import Foundation struct ErrorMessage: Codable { let message: String }
- 로직 추가
// alert창 띄우는 메소드 생성 func showAlert(message: String) { let alert = UIAlertController(title: "에러", message: message, preferredStyle: .alert) alert.addAction(UIAlertAction(title: "확인", style: .default, handler: nil)) self.present(alert, animated: true, completion: nil) } func getCurrentWeather(cityName: String) { guard let url = URL(string: "https://api.openweathermap.org/data/2.5/weather?q=\(cityName)&appid=c942f7d5ac33681b9f170227d606626b") else { return } let session = URLSession(configuration: .default) // 서버로 데이터 요청, 응답 받기 // dataTask가 api를 호출 -> 서버에서 응답이 오면 completionHandler가 호출됨 // data:서버에서 응답받은 json데이터, response:http헤더 및 상태 코드와 같은 응답데이터, error: 요청에 실패하면 error객체/요청에 성공하면 nil반환 session.dataTask(with: url, completionHandler: { [weak self] data, response, error in let successRange = (200..<300) guard let data = data, error == nil else { return } let decoder = JSONDecoder() // json객체에서 데이터 유형의 사용자정의 타입의 인스턴스로 변환 // 통신 실패시 alert창 if let response = response as? HTTPURLResponse, successRange.contains(response.statusCode) { guard let WeatherInformation = try? decoder.decode(WeatherInformation.self, from: data) else { return } DispatchQueue.main.async { self?.weaterStackView.isHidden = false self?.configureView(weatherInformation: WeatherInformation) } } else { guard let errorMessage = try? decoder.decode(ErrorMessage.self, from: data) else { return } // 메인스레드에서 alert띄우기 DispatchQueue.main.sync { self?.showAlert(message: errorMessage.message) } } }).resume() }
'iOS > 패스트캠퍼스(앱제작)' 카테고리의 다른 글
spotify 로그인 구현 (0) 2022.05.15 코로나 현황판 앱 (0) 2022.05.08 타이머 앱 (0) 2022.05.08 일기장 앱 (0) 2022.04.20 할일 리스트(To-Do List) 앱 (0) 2022.04.03