이번 강의에서는 위치 기반 날씨 앱을 만들었다.
UITextField,프로토콜 및 delegate, extension, URL 세션과의 네트워킹, JSON 데이터 구문 분석.
TextField로 할 수 있는 것은 사용자가 입력한 것을 데이터로 사용이 가능하다. 그렇다면 키보드가 나올텐데, 키보드를 닫고 열고 할 수 있는 코드를 배웠다.
@IBAction func searchPressed(_ sender: UIButton) {
// 검색 버튼을 누르면 endEditing 되게끔
// 그러면 키보드가 내려감
searchTextField.endEditing(true)
print(searchTextField.text!)
}
searchTextField.endEditing(true)
입력이 끝났을 경우 키보드가 닫히는 코드이다.
// TextField에서 Return 버튼을 눌렀을 경우(여기선 Go버튼)
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
print(searchTextField.text!)
searchTextField.endEditing(true)
return true
}
키보드의 리턴 버튼을 눌렀을 경우에도 같이 키보드가 닫힌다.
이 차이점은 이미 다른 글로 소개한 바 있다.
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
// 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는 써보면서 더 알아보자.
익스텐션은 기존에 있는 것들에 기능을 추가할 수도 있고, 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
으로 묶을수가 있다.
이 또한 다른 글로 정리하였다. 글이 상당히 길어 링크로 대체한다.
iOS API JSON 데이터 가져오기 (날씨 API)
iOS API JSON Parsing 하기 (날씨 API)
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 등 여러 것들을 이론만 아는 수준에서 사용하는 것으로 한단계 업그레이드 한 기분이다. 하지만 아직 손에 익지 않았으니 여러곳에서 사용해봐야 할듯 하다.
로케이션도 손을 댔다. 사실 강의 전에 만져본 기록이 있어서 이 부분은 그렇게 어렵지는 않았다.
점점 하나하나 배우는 느낌이 좋다. 더 열심히 해보자.