Loading...
Loading...
Fetch current, hourly, and daily weather forecasts and display required attribution using WeatherKit. Use when integrating weather data, showing forecasts, handling weather alerts, displaying Apple Weather attribution, or querying historical weather statistics in iOS apps.
npx skill4agent add dpearson2699/swift-ios-skills weatherkitWeatherServiceNSLocationWhenInUseUsageDescriptionimport WeatherKit
import CoreLocationSendablelet weatherService = WeatherService.shared
// or
let weatherService = WeatherService()Weatherfunc fetchCurrentWeather(for location: CLLocation) async throws -> CurrentWeather {
let weather = try await weatherService.weather(for: location)
return weather.currentWeather
}
// Using the result
func displayCurrent(_ current: CurrentWeather) {
let temp = current.temperature // Measurement<UnitTemperature>
let condition = current.condition // WeatherCondition enum
let symbol = current.symbolName // SF Symbol name
let humidity = current.humidity // Double (0-1)
let wind = current.wind // Wind (speed, direction, gust)
let uvIndex = current.uvIndex // UVIndex
print("\(condition): \(temp.formatted())")
}func fetchHourlyForecast(for location: CLLocation) async throws -> Forecast<HourWeather> {
let weather = try await weatherService.weather(for: location)
return weather.hourlyForecast
}
// Iterate hours
for hour in hourlyForecast {
print("\(hour.date): \(hour.temperature.formatted()), \(hour.condition)")
}func fetchDailyForecast(for location: CLLocation) async throws -> Forecast<DayWeather> {
let weather = try await weatherService.weather(for: location)
return weather.dailyForecast
}
// Iterate days
for day in dailyForecast {
print("\(day.date): \(day.lowTemperature.formatted()) - \(day.highTemperature.formatted())")
print(" Condition: \(day.condition), Precipitation: \(day.precipitationChance)")
}WeatherQueryfunc fetchExtendedForecast(for location: CLLocation) async throws -> Forecast<DayWeather> {
let startDate = Date.now
let endDate = Calendar.current.date(byAdding: .day, value: 10, to: startDate)!
let forecast = try await weatherService.weather(
for: location,
including: .daily(startDate: startDate, endDate: endDate)
)
return forecast
}func fetchAlerts(for location: CLLocation) async throws -> [WeatherAlert]? {
let weather = try await weatherService.weather(for: location)
return weather.weatherAlerts
}
// Process alerts
if let alerts = weatherAlerts {
for alert in alerts {
print("Alert: \(alert.summary)")
print("Severity: \(alert.severity)")
print("Region: \(alert.region)")
if let detailsURL = alert.detailsURL {
// Link to full alert details
}
}
}WeatherQuerylet current = try await weatherService.weather(
for: location,
including: .current
)
// current is CurrentWeatherlet (current, hourly, daily) = try await weatherService.weather(
for: location,
including: .current, .hourly, .daily
)
// current: CurrentWeather, hourly: Forecast<HourWeather>, daily: Forecast<DayWeather>let minuteForecast = try await weatherService.weather(
for: location,
including: .minute
)
// minuteForecast: Forecast<MinuteWeather>? (nil if unavailable)| Query | Return Type | Description |
|---|---|---|
| | Current observed conditions |
| | 25 hours from current hour |
| | 10 days from today |
| | Next-hour precipitation (limited regions) |
| | Active weather alerts |
| | Dataset availability for location |
func fetchAttribution() async throws -> WeatherAttribution {
return try await weatherService.attribution
}import SwiftUI
import WeatherKit
struct WeatherAttributionView: View {
let attribution: WeatherAttribution
@Environment(\.colorScheme) private var colorScheme
var body: some View {
VStack(spacing: 8) {
// Display the Apple Weather mark
AsyncImage(url: markURL) { image in
image
.resizable()
.scaledToFit()
.frame(height: 20)
} placeholder: {
EmptyView()
}
// Link to the legal attribution page
Link("Weather data sources", destination: attribution.legalPageURL)
.font(.caption2)
.foregroundStyle(.secondary)
}
}
private var markURL: URL {
colorScheme == .dark
? attribution.combinedMarkDarkURL
: attribution.combinedMarkLightURL
}
}| Property | Use |
|---|---|
| Apple Weather mark for light backgrounds |
| Apple Weather mark for dark backgrounds |
| Square Apple Weather logo |
| URL to the legal attribution web page |
| Text alternative when a web view is not feasible |
| Weather data provider name |
func checkAvailability(for location: CLLocation) async throws {
let availability = try await weatherService.weather(
for: location,
including: .availability
)
// Check specific dataset availability
if availability.alertAvailability == .available {
// Safe to fetch alerts
}
if availability.minuteAvailability == .available {
// Minute forecast available for this region
}
}// WRONG: Show weather data without attribution
VStack {
Text("72F, Sunny")
}
// CORRECT: Always include attribution
VStack {
Text("72F, Sunny")
WeatherAttributionView(attribution: attribution)
}// WRONG: Fetches everything
let weather = try await weatherService.weather(for: location)
let temp = weather.currentWeather.temperature
// CORRECT: Fetch only current conditions
let current = try await weatherService.weather(
for: location,
including: .current
)
let temp = current.temperaturenil// WRONG: Force-unwrap minute forecast
let minutes = try await weatherService.weather(for: location, including: .minute)
for m in minutes! { ... } // Crash in unsupported regions
// CORRECT: Handle nil
if let minutes = try await weatherService.weather(for: location, including: .minute) {
for m in minutes { ... }
} else {
// Minute forecast not available for this region
}WeatherService// WRONG: No WeatherKit capability configured
let weather = try await weatherService.weather(for: location) // Throws
// CORRECT: Enable WeatherKit in Xcode Signing & Capabilities
// and in the Apple Developer portal for your App ID// WRONG: Fetch on every view appearance
.task {
let weather = try? await fetchWeather()
}
// CORRECT: Cache with a staleness interval
actor WeatherCache {
private var cached: CurrentWeather?
private var lastFetch: Date?
func current(for location: CLLocation) async throws -> CurrentWeather {
if let cached, let lastFetch,
Date.now.timeIntervalSince(lastFetch) < 600 {
return cached
}
let fresh = try await WeatherService.shared.weather(
for: location, including: .current
)
cached = fresh
lastFetch = .now
return fresh
}
}legalAttributionTextWeatherQueryweather(for:)WeatherAvailabilityCLLocationMeasurement.formatted()references/weatherkit-patterns.md