ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 날씨 앱
    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

    댓글

Designed by Tistory.