π¦ WeatherKit μμ μ 볡
Apple 곡μ λ μ¨ APIλ‘ μ νν λ μ¨ μ 보λ₯Ό μ 곡νμΈμ. νμ¬ λ μ¨λΆν° 10μΌ μ보κΉμ§!
β¨ WeatherKitμ΄λ?
WeatherKitμ Appleμ΄ μ 곡νλ 곡μ λ μ¨ APIμ λλ€. iOS 16+μμ μ¬μ© κ°λ₯νλ©°, μ 50λ§ κ±΄κΉμ§ 무λ£μ λλ€.
π μ€μ (Apple Developer)
Setup Steps
1. Apple Developer μ½μ μ μ
2. Certificates, Identifiers & Profiles
3. Keys νμμ μ ν€ μμ±
4. "WeatherKit" 체ν¬λ°μ€ νμ±ν
5. λ€μ΄λ‘λν .p8 νμΌ μμ νκ² λ³΄κ΄
β οΈ ν€λ ν λ²λ§ λ€μ΄λ‘λ κ°λ₯νλ―λ‘ λ°±μ
νμ!π‘οΈ νμ¬ λ μ¨ κ°μ Έμ€κΈ°
CurrentWeather.swift
import WeatherKit import CoreLocation let weatherService = WeatherService() func fetchCurrentWeather(for location: CLLocation) async throws -> CurrentWeather { let weather = try await weatherService.weather(for: location) return weather.currentWeather } // μ¬μ© μμ Task { let location = CLLocation(latitude: 37.5665, longitude: 126.9780) // μμΈ let current = try await fetchCurrentWeather(for: location) print("μ¨λ: \(current.temperature.value)Β°C") print("μ²΄κ° μ¨λ: \(current.apparentTemperature.value)Β°C") print("μ΅λ: \(current.humidity * 100)%") print("λ μ¨: \(current.condition.description)") print("λ°λ: \(current.wind.speed.value) m/s") }
π μκ°λ³ μ보 (24μκ°)
HourlyForecast.swift
func fetchHourlyForecast(for location: CLLocation) async throws -> Forecast<HourWeather> { let weather = try await weatherService.weather(for: location) return weather.hourlyForecast } // μ¬μ© μμ Task { let location = CLLocation(latitude: 37.5665, longitude: 126.9780) let hourly = try await fetchHourlyForecast(for: location) // λ€μ 12μκ° μ보 for hour in hourly.prefix(12) { print("\(hour.date) - \(hour.temperature.value)Β°C - \(hour.condition)") } }
π μΌλ³ μ보 (10μΌ)
DailyForecast.swift
func fetchDailyForecast(for location: CLLocation) async throws -> Forecast<DayWeather> { let weather = try await weatherService.weather(for: location) return weather.dailyForecast } // μ¬μ© μμ Task { let location = CLLocation(latitude: 37.5665, longitude: 126.9780) let daily = try await fetchDailyForecast(for: location) for day in daily { print("\(day.date)") print("μ΅κ³ : \(day.highTemperature.value)Β°C") print("μ΅μ : \(day.lowTemperature.value)Β°C") print("κ°μ νλ₯ : \(day.precipitationChance * 100)%") print("μΌμΆ: \(day.sun.sunrise!)") print("μΌλͺ°: \(day.sun.sunset!)") } }
β λΆλ³ κ°μ μ보 (λ€μ 1μκ°)
MinuteForecast.swift
func fetchMinuteForecast(for location: CLLocation) async throws -> Forecast<MinuteWeather>? { let weather = try await weatherService.weather(for: location) return weather.minuteForecast } // μ¬μ© μμ (λ―Έκ΅λ§ μ§μ) Task { let location = CLLocation(latitude: 37.7749, longitude: -122.4194) // μνλμμ€μ½ if let minute = try await fetchMinuteForecast(for: location) { for m in minute { print("\(m.date) - κ°μ κ°λ: \(m.precipitation.value)") } } else { print("λΆλ³ μ보 μ§μ μ ν¨ (λ―Έκ΅λ§ κ°λ₯)") } }
π¨ κΈ°μ 경보
WeatherAlerts.swift
func fetchWeatherAlerts(for location: CLLocation) async throws -> [WeatherAlert]? { let weather = try await weatherService.weather(for: location) return weather.weatherAlerts } // μ¬μ© μμ Task { let location = CLLocation(latitude: 37.5665, longitude: 126.9780) if let alerts = try await fetchWeatherAlerts(for: location) { for alert in alerts { print("β οΈ \(alert.summary)") print("경보 μμ€: \(alert.severity)") print("λ°ν¨: \(alert.effectiveTime)") print("λ§λ£: \(alert.expireTime)") } } }
π€ λ μ¨ μν (WeatherCondition)
WeatherCondition.swift
import WeatherKit func getWeatherIcon(_ condition: WeatherCondition) -> String { switch condition { case .clear: return "sun.max.fill" case .cloudy: return "cloud.fill" case .rain: return "cloud.rain.fill" case .snow: return "snow" case .sleet: return "cloud.sleet.fill" case .hail: return "cloud.hail.fill" case .thunderstorms: return "cloud.bolt.fill" case .heavyRain: return "cloud.heavyrain.fill" case .strongStorms: return "cloud.bolt.rain.fill" case .foggy: return "cloud.fog.fill" case .windy: return "wind" @unknown default: return "questionmark" } } // νκΈ μ€λͺ func getWeatherDescription(_ condition: WeatherCondition) -> String { switch condition { case .clear: return "λ§μ" case .cloudy: return "νλ¦Ό" case .rain: return "λΉ" case .snow: return "λ" case .thunderstorms: return "λμ°" @unknown default: return "μ μ μμ" } }
π± SwiftUI ν΅ν©
WeatherView.swift
import SwiftUI import WeatherKit import CoreLocation @Observable class WeatherViewModel { var currentWeather: CurrentWeather? var hourlyForecast: Forecast<HourWeather>? var isLoading = false let weatherService = WeatherService() func loadWeather(for location: CLLocation) async { isLoading = true defer { isLoading = false } do { let weather = try await weatherService.weather(for: location) currentWeather = weather.currentWeather hourlyForecast = weather.hourlyForecast } catch { print("λ μ¨ λ‘λ μ€ν¨: \(error)") } } } struct WeatherView: View { var viewModel = WeatherViewModel() let location = CLLocation(latitude: 37.5665, longitude: 126.9780) var body: some View { ScrollView { VStack(spacing: 20) { if viewModel.isLoading { ProgressView() } else if let current = viewModel.currentWeather { // νμ¬ λ μ¨ VStack { Image(systemName: getWeatherIcon(current.condition)) .font(.system(size: 80)) .symbolRenderingMode(.multicolor) Text("\(Int(current.temperature.value))Β°") .font(.system(size: 72, weight: .thin)) Text(getWeatherDescription(current.condition)) .font(.title2) } // μκ°λ³ μ보 if let hourly = viewModel.hourlyForecast { ScrollView(.horizontal, showsIndicators: false) { HStack(spacing: 16) { ForEach(hourly.prefix(24), id: \.date) { hour in VStack { Text(hour.date, format: .dateTime.hour()) .font(.caption) Image(systemName: getWeatherIcon(hour.condition)) .font(.title2) Text("\(Int(hour.temperature.value))Β°") .font(.headline) } .frame(width: 60) } } .padding() } } } } } .task { await viewModel.loadWeather(for: location) } } }
β‘ μλ¬ μ²λ¦¬
ErrorHandling.swift
do { let weather = try await weatherService.weather(for: location) } catch { if let weatherError = error as? WeatherError { switch weatherError { case .locationUnavailable: print("μμΉ μ 보 μμ") case .locationOutOfRange: print("μ§μνμ§ μλ μ§μ") case .networkUnavailable: print("λ€νΈμν¬ μ°κ²° μμ") @unknown default: print("μ μ μλ μλ¬") } } }
π° μ¬μ©λ μ ν
Rate Limits
WeatherKit API μ ν:
- μ 50λ§ κ±΄ 무λ£
- μ΄κ³Ό μ: $0.0001/call (100λ§ κ±΄λΉ $100)
- Rate limit: λΆλΉ 500 requests
β οΈ μΊμ±μ μ κ·Ή νμ©νμΈμ!
- νμ¬ λ μ¨: 10λΆ μΊμ
- μκ°λ³ μ보: 30λΆ μΊμ
- μΌλ³ μ보: 1μκ° μΊμπ‘ HIG 체ν¬λ¦¬μ€νΈ
β
Apple μ 곡 λ μ¨ μμ΄μ½ μ¬μ© (SF Symbols)
β
λ°μ΄ν° μΆμ² νμ ("Weather data provided by Apple")
β
μ μ ν μΊμ±μΌλ‘ API νΈμΆ μ΅μν
β
λ‘λ© μν νμ
β
λ€νΈμν¬ μλ¬ μ²λ¦¬