Clima (4)
날씨 정보를 파싱하여 뷰 컨트롤러에 전달
우선 WeatherData로 돌아가서 이젠 우리가 다시 swift로 encoding을 해줘야 하므로
Decodable, Encodable을 같이 사용하면 다음과 같은 에러가 발생한다.
1
2
3
4
5
struct WeatherData : Decodable, Encodable {
let name : String
let main : Main
let weather : [Weather]
}
이때 우리가 사용할것이 바로 Codable 프로토콜인데,
Decodable과 Encodable프로토콜을 합친것이 바로 Codable 프로토콜이다.
그래서 모든 WeatherData.swift에 있는 구조체들을 모두 바꿔주자
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import Foundation
struct WeatherData : Codable {
let name : String
let main : Main
let weather : [Weather]
}
struct Main : Codable {
let temp : Double
}
struct Weather : Codable {
let description : String
let id : Int
}
그리고 parseJSON 함수에서 만들어진 값을 performRequest의
1
2
3
if let safeData = data {
self.parseJSON(weatherData: safeData)
}
이 부분에 값을 전달하려고 한다.
이젠 리턴을 해야하므로 함수를 살짝 수정 해주자
func parseJSON(weatherData: Data) -> WeatherModel
이렇게 리턴타입을 명시해준다.
1
2
3
4
5
6
7
8
9
10
11
12
func parseJSON(weatherData: Data) -> WeatherModel? {
let decoder = JSONDecoder()
do {
생략
return weather
print(weather.temperatureString)
} catch {
print(error)
}
}
이렇게 리턴을 하도록 했다.
그런데 만약 디코딩 과정이 실패한다면??
우리는 아무것도 없는걸 리턴해줘야한다. 그렇다면 아무것도 없다는게 무엇일까?
바로 nil
이다!
그래서 다음과 같이 nil을 리턴한다는걸 catch 구문에 적어주자
1
2
3
4
catch {
print(error)
return nil
}
이렇게 nil을 반환한다는건?
그렇다 바로 WeatherModel의 DataType이 Optional 이어야 한다는 뜻이다.
func parseJSON(weatherData: Data) -> WeatherModel
이었던것에 ?를 하나 더 붙여서 옵셔널 타입으로 전환 해주자
func parseJSON(weatherData: Data) -> WeatherModel?
완성.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
func parseJSON(weatherData: Data) -> WeatherModel? {
let decoder = JSONDecoder()
do {
let decodedData = try decoder.decode(WeatherData.self, from: weatherData)
let id = decodedData.weather[0].id
let temp = decodedData.main.temp
let name = decodedData.name
let weather = WeatherModel(conditionId: id, cityName: name, temperature: temp)
return weather
print(weather.temperatureString)
} catch {
print(error)
return nil
}
}
그럼 이제 다시 performRequest로 거슬러 올라가서…
우리가 parseJSON 으로 리턴하는값이 옵셔널이 되었으므로 옵셔널 바인딩을 해주어 순수한 weatherModel 타입이 리턴되도록 하자
1
2
if let safeData = data {
if let weather = self.parseJSON(weatherData: safeData) {
if let 한번 더 감싸준다.
그리고 이 weather를 WeatherViewController로 전달해야 하므로 viewcontroller를 인스턴스화 하자.
let weatherVC = WeatherViewController()
그리고 그값을 받아올 함수를 하나 만들어 준다.
우선 온도만 잘 받아오게끔 해본다.
1
2
3
4
// WeatherViewController
func didUpdateWeather(weather : WeatherModel) {
print(weather.temperature)
}
그리고 다시 WeatherManager로 돌아가서,
weatherVC.didUpdateWeather(weather: weather)
vc에 전달할 weather값을 보낸다.
우리가 여태 배웠던 delegate 나 protocol을 생각해보고, 이걸 한다면 우리는 WeatherManager를 한번만 사용하게 될것이다.
마치 일회용품처럼
하지만 우리는 이 프로젝트를 제한해야한다. 이후에 또 프로젝트를 할때 날씨 데이터가 필요하다면 재사용 하면 된다.
그렇게 재사용 하는 방법은 다른 개체와 연결하는 특정 코드를 사용하지 않으면 된다.
1
2
let weatherVC = WeatherViewController()
weatherVC.didUpdateWeather(weather: weather)
해당구문을 일단 지워두고 delegate를 사용하고, delegate의 data Type은 WeatherManagerDelegate?로 한다.
그리고 delegate.didUpdateWeather 를 통해 값을 전달하고, 그값을 출력하게 해보자
여기선 일단 프로토콜을 만들어 주어야한다.
1
2
3
protocol WeatherManagerDelegate{
func didUpdateWeather(weather: WeatherModel)
}
클로져 안에 있기에 delegate 앞에 self를 붙여주었다.
1
self.delegate?.didUpdateWeather(weather: weather)
그리고 뷰컨트롤러로 돌아가 우리가 만든 프로토콜을 적용시켜주었다.
1
class WeatherViewController: UIViewController, UITextFieldDelegate, WeatherManagerDelegate {
여기까지는 내가 생각한것과 좀 비슷했다.
주의해야 하는것이. 우리가 전에 UITextFieldDelegate 프로토콜을 쓸때도 viewDidLoad()에
searchTextField.delegate = self
이걸 사용해주었다.
이번에도 weatherManager.delegate = self
를 해줘야한다!
1
2
3
4
5
6
override func viewDidLoad() {
super.viewDidLoad()
weatherManager.delegate = self
searchTextField.delegate = self
}
실행해보자!
잘된다.
이제 이 프로젝트에서 우리는 WeatherManager를 일일이 작동 시키는 것이 아니라, 프로토콜을 사용하였고, viewdidload에서 그 프로토콜을 사용 가능하게 하였기에, 어떤 클래스와 상관없이 날씨 데이터를 받아 올 수 있게 되었다.