[Swift] '지구 온도계' 프로젝트(4)

Erick·2023년 1월 14일
0
post-thumbnail

'지구 온도계' 프로젝트(4)


드디어 끝났다!

드디어! 프로젝트가 끝났습니다!!
저번 포스팅 이후 일주일도 안 돼서 이렇게 블로그를 적고 있네요.
사실 프로젝트가 아예 끝났다고 하기는 이르지만, 처음 기획했던 기능을 모두 구현하였습니다.
이후에 앱스토어 출시 그리고 코드를 '디자인패턴으로 정리하는 것은 조금 시간이 더 걸릴 것 같습니다.

기능 구현의 마무리

데이터 저장 및 삭제

저번 포스팅에서 설명한 지역 추가에서 이어지는 데이터 저장 기능을 구현하였습니다.

데이터 저장에는 여러 방법이 있지만 저희 앱에서는 간단히 추가한 지역만을 저장하면 되기 때문에 UserDefault를 사용하여 데이터를 저장하였습니다.

//지역 추가 함수
func addVC() {
    //userDefault에 저장
    if var defaultMapItemArray = UserDefaults.standard.array(forKey: "key") as? [[String]] {
        defaultMapItemArray.append(mapItemArray)
        UserDefaults.standard.set(defaultMapItemArray, forKey: "key")
    }
}

지역 추가 함수 내에 위 코드와 같이 UserDefaults.standard.set으로 UserDefault에 데이터를 추가하는 코드를 넣었습니다.
set 함수로 데이터를 세팅할 때는 새로운 데이터만 세팅이 되니, UserDefaults.standard.array로 기존에 저장된 배열을 불러와 .append로 새로운 요소를 추가하고 새로 세팅하는 방식으로 구현하였습니다.

//지역 삭제 함수
func delVC() {
    //default부분 삭제
    if var defaultMapItemArray = UserDefaults.standard.array(forKey: "key") as? [[String]] {
        defaultMapItemArray.remove(at: currentIndex - 2)
        UserDefaults.standard.set(defaultMapItemArray, forKey: "key")
    }    
}

삭제 메서드도 지역 추가 메서드와 같이 UserDefault의 데이터를 불러온 뒤 불러온 데이터에서 .remove를 하고 .set으로 다시 세팅하는 방식으로 구현하였습니다.

//첫화면에서 나의 위치와 지역을 세팅하는 함수
func weatherCard() {
    individualPageViewControllerList.append(DefaultWeatherCardViewController.getInstance())
    //userDefault를 사용하여 데이터 세팅
    if let defaultMapItemArray = UserDefaults.standard.array(forKey: "key") as? [[String]] {
        for i in 0..<defaultMapItemArray.count {
            let mapItem = defaultMapItemArray[i]
            individualPageViewControllerList.append(WeatherCardViewController.getInstance(locality: mapItem[0], country: mapItem[1], latitude: mapItem[2], longitude: mapItem[3]))
        }
    }
    setViewControllers([individualPageViewControllerList[1]], direction: .forward, animated: false, completion: nil)
}

그리고 처음 앱을 켜 데이터를 세팅할 때는 UserDefault에 저장된 배열 내 요소 하나하나에 접근하여 ViewController를 저장하는 방식으로 구현하였습니다.
처음에는 직접 ViewController로 이루어진 배열을 저장하려 했으나, UserDefault에는 ViewController를 저장할 수 없어 인스턴스 추가에 필요한 파라미터들이 담긴 배열을 추가하는 방식으로 바꾸었습니다.

나의 위치 데이터 받아오기

이 기능은 제가 하기로 한 기능은 아니었지만 다른 팀원이 바쁜 관계로 제가 구현을 하게 되었습니다.

//현재위치 받아오기
DispatchQueue.global().async {
    //위치 사용을 허용하면 현재 위치 정보를 가져옴
    if CLLocationManager.locationServicesEnabled() {
        //위치 받아오기
        locationManager.startUpdatingLocation()
        self.myLocation = CLLocation(latitude: (locationManager.location?.coordinate.latitude)!, longitude: (locationManager.location?.coordinate.longitude)!)
        self.location = "나의 위치"
    } else {
        self.location = "서울"
    }
}

코드는 굉장히 간단하죠?
사용자가 위치 사용을 허용하면 현재 위치 정보를 받아오고 이것을 myLocation에 저장하도록 하였습니다.
그리고 허용하지 않는다면 Default를 서울로 하도록 하였습니다.
(myLocation의 기본값도 서울의 위도 경도로 되어있습니다.)

'작년 오늘'

'작년 오늘'은 작년 오늘의 평균기온을 가지고 오는 기능입니다.

현재 날씨는 WeatherKit을 사용하여 받아오고 있고 WeatherKit에서는 작년 날씨는 제공해 주지 않아 기상청 API를 사용하였습니다.

//YearWeatherService.swift

class YearWeatherService {
    //작년 날씨를 불러오는 함수
    func getWeather(regionCode: Int, date: Int, completion: @escaping (Result<YearWeatherData, NetworkError>) -> Void) {
        // API 호출을 위한 URL
        let url = URL(string: "http://apis.data.go.kr/1360000/AsosDalyInfoService/getWthrDataList?serviceKey=\(apiKey)&numOfRows=1&pageNo=1&dataType=JSON&dataCd=ASOS&dateCd=DAY&startDt=\(date)&endDt=\(date)&stnIds=\(regionCode)")
        guard let url = url else {
            return completion(.failure(.badUrl))
        }
        
        URLSession.shared.dataTask(with: url) { data, response, error in
            guard let data = data, error == nil else {
                return completion(.failure(.noData))
            }
            
            // Data 타입으로 받은 리턴을 디코드
            let weatherResponse = try? JSONDecoder().decode(YearWeatherData.self, from: data)
            
            // 성공
            if let weatherResponse = weatherResponse {
                completion(.success(weatherResponse)) // 성공한 데이터 저장
            } else {
                completion(.failure(.decodingError))
            }
        }.resume()
    }
}

YearWeatherService Class 내부에 getWeather 메서드를 생성하여 URL에 필요한 regionCode, date를 파라미터로 받아 API를 호출하여 데이터를 저장하도록 구현하였습니다.

//작년날씨 데이터 요청
func yearWeatherData(regionCode: Int, date: Int) {
    // data fetch(데이터 요청)
    YearWeatherService().getWeather(regionCode: regionCode, date: date) { result in
        switch result {
        case .success(let weatherResponse):
            DispatchQueue.main.async {
                let array = weatherResponse.response.body.items.item
                //받아온 데이터 저장
                let itemList = array[0]
                //평균 온도 데이터 저장
                let yearAvgTemp = Double(itemList["avgTa"]!)!
                self.yearAvgTempLabel.text = "\(Int(round(yearAvgTemp)))º"
            }
        case .failure(_ ):
            print("error")
        }
    }
}

그리고 ViewController에서 YearWeatherService.getWeather()을 실행하여 API를 통해 받아온 데이터를 Label로 보여주도록 설정하였습니다.

'작년 오늘' 날짜, 지역 설정

캘린더와 지역에서 보내온 날짜와 지역 코드로 다른 날짜 다른 지역의 평균기온도 확인할 수 있습니다.

//CalenderViewController.swift

@IBAction func selectButtonTapped(_ sender: UIButton) {
    //notification post전송, date 데이터 전달
    NotificationCenter.default.post(name: Notification.Name("setDate"), object: date)
    self.dismiss(animated: true)
}
//RegionViewController.swift

@IBAction func selectButtonTapped(_ sender: UIButton) {
    //notification post전송, regionList 데이터 전달
    NotificationCenter.default.post(name: Notification.Name("setRegion"), object: regionList)
    self.dismiss(animated: true)
}

저번에 소개한 지역 추가 기능과 마차가지로 Notification을 사용하여 데이터 전달 및 메서드 실행을 하였습니다.


마치며

프로젝트를 하며 생각보다 의견 조율을 하고 새로운 기능을 구현하는 게 어려운 것을 느꼈고 이를 통해 더 많이 성장한 시간이었던 것 같네요.
그리고 공부의 필요성을 더 느끼는 시간이었습니다.
이후에는 '디자인 패턴'을 공부할 생각이고, 개인 프로젝트도 생각 중입니다.
1월이 지나기 전에 새로운 포스팅으로 돌아오겠습니다.
그동안 '지구 온도계' 프로젝트 포스팅을 봐주셔서 감사합니다.

profile
iOS Developer

0개의 댓글