iOS/Swift

[Swift] WeatherKit을 사용한 간단한 날씨 앱 만들기

JIN-JJS 2024. 3. 6. 16:56

1. WeatherKit이란?

- WWDC2022

Apple Developer WeahterKit

Apple의 WeatherKit 소개글
WeahterKit 제공 데이터

(1) WeatherKit의 장점

- 오픈 API를 사용하지 않아도 Apple 날씨 서비스에 의해 구동

- 앱에 시기적절하게 사용자 생활권의 날씨 정보를 제공하는 데 필요한 모든 데이터를 제공

- Swift의 현대적인 구문을 활용하는 Swift API가 포함

- Swift 동시 실행을 통해 코드 몇 줄로 날씨 데이터를 손쉽게 요청

- 사용자 정보를 침해하지 않고 위치정보는 일기 예보 제공 목적으로만 사용됨

 

(2) WeatherKit의 단점

- Apple 개발자 계정이 있어야 함

- 개발자 계정이더라도 매달 50만 API 호출 건수까지만 제공

 

 

2. WeaherKit 세팅 

(1) App ID 생성

- 인증서 페이지로 이동

- 현재 생성하는 Bundle ID는 프로젝트의 Bundle ID와 동일해야 합니다.

 

(2) Key 생성

 

(3) Xcode 설정

- (1)에서 생성했던 Bundle ID는 프로젝트의 Bundle ID와 동일해야 합니다.

- Capability에서 WeatherKit을 추가합니다.

 

 

3. 날씨 정보 가져오기

(1) WeatherKitManager

import WeatherKit

@MainActor class WeatherKitManager: ObservableObject {
    @Published var weather: Weather?
        
    func getWeather(latitude: Double, longitude: Double) {
            Task {
                do {
                    weather = try await Task.detached(priority: .userInitiated) {
                        return try await WeatherService.shared.weather(for: .init(latitude: latitude, longitude: longitude))  
                    }.value
                } catch {
                  fatalError("\(error)")
                }
            }
        }
        
        var symbol: String {
            weather?.currentWeather.symbolName ?? "xmark"
        }
        
        var temp: String {
            let temp = weather?.currentWeather.temperature
            let convertedTemp = temp?.converted(to: .celsius).description
            return convertedTemp ?? "Loading Weather Data"
        }
    
}

- getWeather : 위도/경도의 정보를 넘겨주어 날씨 정보를 가져오는 함수 

- temp : 섭씨 변환 후 return 값으로 삼항연산자를 이용한 온도 표기 혹은 "Loading Weather Data"를 띄워줌

 

참고한 코드 Warning 수정(현재 수정함)

- Warning : 'async(priority:operation:)' is deprecated: `async` was replaced by `Task.init` and will be removed shortly.

- Fix : async -> Task

 

(2) LocationManager

import CoreLocation

class LocationManager: NSObject, ObservableObject, CLLocationManagerDelegate {
    
    var locationManager = CLLocationManager()
    @Published var authorisationStatus: CLAuthorizationStatus?
    
    var latitude: Double {
        locationManager.location?.coordinate.latitude ?? 37.596970
    }
    
    var longitude: Double {
        locationManager.location?.coordinate.longitude ?? -127.036119
    }
    
    override init() {
        super.init()
        locationManager.delegate = self
    }
    
    func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {
        switch manager.authorizationStatus {
        case .authorizedWhenInUse:
            // location services available
            authorisationStatus = .authorizedWhenInUse
            locationManager.requestLocation()
            break
            
        case .restricted:
            authorisationStatus = .restricted
            break
            
        case .denied:
            authorisationStatus = .denied
            break
            
        case .notDetermined:
            authorisationStatus = .notDetermined
            manager.requestWhenInUseAuthorization()
            break
            
        default:
            break
        }
    }
    
    func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        
    }
    
    func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
        print("\(error.localizedDescription)")
    }
    
}

 

(3) ContentView

struct ContentView: View {
    @ObservedObject var weatherKitManager = WeatherKitManager()
    
    @StateObject var locationManager = LocationManager()
    
    var body: some View {
        
        if locationManager.authorisationStatus == .authorizedWhenInUse {
            // Create your view
            VStack {
                Label(weatherKitManager.temp, systemImage: weatherKitManager.symbol)
            }
            .task {
                 weatherKitManager.getWeather(latitude: locationManager.latitude, longitude: locationManager.longitude)
            }
        } else {
            // Create your alternate view
            Text("Error loading location")
        }
        
    }
}

- 삼항연산자(if/else)로 위치 정보 승인 화면과 거절한 화면을 나눔

 

참고한 코드 Warning 수정(현재 수정함)

- Warning : No 'async' operations occur within 'await' expression

- Fix : await weatherKitManager.getWeather(latitude: --.--, longitude: --.--)

           -> weatherKitManager.getWeather(latitude: --.--, longitude: --.--)

 

 

4. 결과

(1) 테스트 화면

- 가져온 날씨 정보는 날씨 기본앱의 날씨와 비교했는데 동일하게 잘 출력되고 있다.(값은 반올림)

 

(2) 전체 코드

 

GitHub - JIN-JJS/WeatherApp

Contribute to JIN-JJS/WeatherApp development by creating an account on GitHub.

github.com

 

(3) 참고 영상