9. Weather Location App

Tabber·2021년 7월 31일
0

App

목록 보기
5/6

이번 강의에서는 위치 기반 날씨 앱을 만들었다.

프로젝트로 배운 것들

UITextField,프로토콜 및 delegate, extension, URL 세션과의 네트워킹, JSON 데이터 구문 분석.

UITextField

TextField로 할 수 있는 것은 사용자가 입력한 것을 데이터로 사용이 가능하다. 그렇다면 키보드가 나올텐데, 키보드를 닫고 열고 할 수 있는 코드를 배웠다.

@IBAction func searchPressed(_ sender: UIButton) {
        // 검색 버튼을 누르면 endEditing 되게끔
        // 그러면 키보드가 내려감
        searchTextField.endEditing(true)
        print(searchTextField.text!)
        
    }

searchTextField.endEditing(true)
입력이 끝났을 경우 키보드가 닫히는 코드이다.

ReturnButtonTapped

// TextField에서 Return 버튼을 눌렀을 경우(여기선 Go버튼)
    func textFieldShouldReturn(_ textField: UITextField) -> Bool {
        print(searchTextField.text!)
        searchTextField.endEditing(true)
        return true
    }

키보드의 리턴 버튼을 눌렀을 경우에도 같이 키보드가 닫힌다.

textFieldShouldEditing VS textFieldDidEditing

이 차이점은 이미 다른 글로 소개한 바 있다.
textFieldEndEditing 과 textFieldShouldEndEditing 차이
이 글을 참고해도 괜찮지만, 간단하게 설명하자면

약간 역할이 다르다. ShouldEditing 은 Delegate에게 물어보는 역할이다.
"어이, 입력이 끝났는데 뭐할까?"
반면 DidEditing 은 일방적 통보이다.
"야, 입력 끝나서 그냥 닫았다"
이런 느낌이다.

func textFieldShouldEndEditing(_ textField: UITextField) -> Bool {
        if textField.text != "" {
            return true
        } else {
            textField.placeholder = "Type something"
            return false
        }
    }
    
func textFieldDidEndEditing(_ textField: UITextField) {
        // 아래에 지워지기 전에 도시 이름을 사용하고 싶음
        // searchTextField.text 가 옵셔널 스트링이니까, 
        // 사용하려면 언래핑 해야 한다.
        // 따라서 if let 구문을 사용했다.
        if let city = searchTextField.text {
            weatherManager.fetchWeather(cityName: city)
            
        }
        
        // 입력이 끝났을 때 현재 입력했던것은 자연스레 지움
        searchTextField.text = ""
    }

이렇게 두개의 코드는 사용하는 곳이 좀 다르다.

protocol And delegate

protocol

// Delegate Design Pattern 구현
protocol WeatherManagerDelegate {
    func didUpdateWeather(_ weatherManager: WeatherManger, weather: WeatherModel)
    // weatherManager에서 오류를 전달할때 도와주는 delegate(메서드)
    func didFailWithError(error: Error)
}

프로토콜은 일종의 규약, 제약이다.
"어,,그러니까 이건 꼭 있어야 합니다." 정도로 해석할 수 있다.

위 코드는 날씨를 표시하는 부분에서 꼭 있어야 할 것들을 정의한 것이다.
코드를 보면, 날씨를 업데이트 하는 didUpdateWeather 가 위치해있고,
에러를 반환하는 didFailWithError 가 존재한다.
이렇게 필수요소들을 프로토콜로 정의하고 사용할 수 있다.

delegate
delegate는 대리자 역할을 한다. 그러니까 코드에 직접 접근하는 것이 아닌, 코드간의 소통을 원활하게 하기위한 것이라고 볼 수 있다.

override func viewDidLoad() {
        super.viewDidLoad()
        
        // delegate 를 위치 업데이트 이전에 해줘야 에러가 발생하지 않는다.
        locationManager.delegate = self
        // 사용자에게 위치 권한을 요청하는 팝업이 화면에 표시된다.
        locationManager.requestWhenInUseAuthorization()
        // 위치를 한번만 업데이트 한다.
        locationManager.requestLocation()
        
        // weatherManager 의 delegate를 ViewController로 설정해줘야,
        // delegate의 주소가 제대로 입력이 된다.
        // *중요*
        
        weatherManager.delegate = self
        
        // TextField에서 일어나는 것들에 대해 위 클래스 자신에게 상황을 알려주는 것이다.
        // TextField에서 알림을 받는 컨트롤러는 뷰 컨트롤러를 대리자로 설정하는 것이다.
        // self는 ViewController를 가리킨다
        searchTextField.delegate = self
    }

이렇게 다른 곳에서 일어나는 것들에 대해 self에게 상황을 알려줄 수 있는 것이다.
이렇게 하면 관리가 편하다(?) delegate는 써보면서 더 알아보자.

extension

익스텐션은 기존에 있는 것들에 기능을 추가할 수도 있고, delegate를 기준으로 나눠줄 수도 있다.

extension WeatherViewController: WeatherManagerDelegate {
    // WeatherManagerDelegate 에 필수적으로 들어가 있는
    // didUpdateWeather가 정의되어있는 상태이니 에러가 발생하지 않는다.
    // 그리고 이 메서드가 데이터를 가져오는 곳은 weatherManager 이니 ID에 weatherManager 를 추가해줘야한다.
    func didUpdateWeather(_ weatherManager: WeatherManger, weather: WeatherModel) {
        // 클로저는 백그라운드에서 사용자 인터페이스를 업데이트하기 위해 메인 스레드를 호출해야 합니다.
        DispatchQueue.main.async {
            self.temperatureLabel.text = weather.temperatureString
            self.conditionImageView.image = UIImage(systemName: weather.conditionName)
            self.locationLabel.text = weather.cityName
        }
        
    }
    // Delegate에서 선언한 메서드 정의
    func didFailWithError(error: Error) {
        print(error)
    }
}

보통은 WeatherViewController 안에 위치하는 viewDidLoad() 메서드 안에 있어야 하는 것들을 사용하는 Delegate들에 따라 나눌수가 있다.

위 코드는 날씨정보를 업데이트하는 메서드가 포함된 Delegate이다. 이 또한 extension으로 묶을수가 있다.

URL 세션과의 네트워킹, JSON 데이터 구문 분석.

이 또한 다른 글로 정리하였다. 글이 상당히 길어 링크로 대체한다.
iOS API JSON 데이터 가져오기 (날씨 API)
iOS API JSON Parsing 하기 (날씨 API)

Location Update

import CoreLocation

class WeatherViewController: UIViewController {
    let locationManager = CLLocationManager()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // delegate 를 위치 업데이트 이전에 해줘야 에러가 발생하지 않는다.
        locationManager.delegate = self
        // 사용자에게 위치 권한을 요청하는 팝업이 화면에 표시된다.
        locationManager.requestWhenInUseAuthorization()
        // 위치를 한번만 업데이트 한다.
        locationManager.requestLocation()
        
     }
 }
 
 extension WeatherViewController: CLLocationManagerDelegate {
    func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        // last 는 배열의 마지막 원소, 그리고 last는 옵셔널이기에 if let 구문 사용
        if let location = locations.last {
            // 위치를 받아오는(찾아낸)즉시 찾는걸 중지시킨다.
            locationManager.stopUpdatingLocation()
            let lat = location.coordinate.latitude
            let lon = location.coordinate.longitude
            // 그리고 나서 패치에 해당 좌표를 옮겨준다.
            weatherManager.fetchWeather(latitude: lat, longitude: lon)
        }
        
    }

좌표나 위치 정보를 얻고 싶으면 위 CoreLocation을 import 해야한다.
그리고 난 뒤에 viewDidLoad()에서 위 코드들로 정보를 얻고 extension으로 추가된 기능을 사용할 수 있다.

프로젝트를 진행하며 느낀점, 배운점

API를 올바르게 사용하는것이 너무나도 어려웠고, 지금도 어렵고 익숙하지가 않다. 하지만, 여러번 사용하다보단 익숙해지리라 믿는다. 그리고 extension, delegate 등 여러 것들을 이론만 아는 수준에서 사용하는 것으로 한단계 업그레이드 한 기분이다. 하지만 아직 손에 익지 않았으니 여러곳에서 사용해봐야 할듯 하다.
로케이션도 손을 댔다. 사실 강의 전에 만져본 기록이 있어서 이 부분은 그렇게 어렵지는 않았다.

점점 하나하나 배우는 느낌이 좋다. 더 열심히 해보자.

profile
iOS 정복중인 Tabber 입니다.

0개의 댓글