포스트

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에서 그 프로토콜을 사용 가능하게 하였기에, 어떤 클래스와 상관없이 날씨 데이터를 받아 올 수 있게 되었다.

이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.