weatherkit

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

WeatherKit

WeatherKit

Fetch current conditions, hourly and daily forecasts, weather alerts, and historical statistics using
WeatherService
. Display required Apple Weather attribution. Targets Swift 6.2 / iOS 26+.
使用
WeatherService
获取当前天气状况、逐小时及每日预报、天气警报和历史统计数据。展示要求的Apple Weather版权声明。适配Swift 6.2 / iOS 26+。

Contents

目录

Setup

设置

Project Configuration

项目配置

  1. Enable the WeatherKit capability in Xcode (adds the entitlement)
  2. Enable WeatherKit for your App ID in the Apple Developer portal
  3. Add
    NSLocationWhenInUseUsageDescription
    to Info.plist if using device location
  4. WeatherKit requires an active Apple Developer Program membership
  1. 在Xcode中启用WeatherKit功能(会添加相关权限)
  2. 在Apple开发者后台为你的App ID启用WeatherKit
  3. 如果使用设备位置,在Info.plist中添加
    NSLocationWhenInUseUsageDescription
    字段
  4. WeatherKit要求拥有有效的Apple开发者计划会员资格

Import

导入

swift
import WeatherKit
import CoreLocation
swift
import WeatherKit
import CoreLocation

Creating the Service

创建服务实例

Use the shared singleton or create an instance. The service is
Sendable
and thread-safe.
swift
let weatherService = WeatherService.shared
// or
let weatherService = WeatherService()
使用共享单例或者创建新实例。该服务支持
Sendable
且线程安全。
swift
let weatherService = WeatherService.shared
// 或者
let weatherService = WeatherService()

Fetching Current Weather

获取当前天气

Fetch current conditions for a location. Returns a
Weather
object with all available datasets.
swift
func 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())")
}
获取指定位置的当前天气状况。返回包含所有可用数据集的
Weather
对象。
swift
func fetchCurrentWeather(for location: CLLocation) async throws -> CurrentWeather {
    let weather = try await weatherService.weather(for: location)
    return weather.currentWeather
}

// 使用返回结果
func displayCurrent(_ current: CurrentWeather) {
    let temp = current.temperature  // Measurement<UnitTemperature>
    let condition = current.condition  // WeatherCondition枚举
    let symbol = current.symbolName  // SF Symbol名称
    let humidity = current.humidity  // Double类型(0-1)
    let wind = current.wind  // 风速、风向、阵风信息
    let uvIndex = current.uvIndex  // UVIndex

    print("\(condition): \(temp.formatted())")
}

Forecasts

天气预报

Hourly Forecast

逐小时预报

Returns 25 contiguous hours starting from the current hour by default.
swift
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)")
}
默认返回从当前时间开始的连续25小时的预报。
swift
func fetchHourlyForecast(for location: CLLocation) async throws -> Forecast<HourWeather> {
    let weather = try await weatherService.weather(for: location)
    return weather.hourlyForecast
}

// 遍历逐小时预报
for hour in hourlyForecast {
    print("\(hour.date): \(hour.temperature.formatted()), \(hour.condition)")
}

Daily Forecast

每日预报

Returns 10 contiguous days starting from the current day by default.
swift
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)")
}
默认返回从今日开始的连续10天的预报。
swift
func fetchDailyForecast(for location: CLLocation) async throws -> Forecast<DayWeather> {
    let weather = try await weatherService.weather(for: location)
    return weather.dailyForecast
}

// 遍历每日预报
for day in dailyForecast {
    print("\(day.date): \(day.lowTemperature.formatted()) - \(day.highTemperature.formatted())")
    print("  天气状况: \(day.condition), 降水概率: \(day.precipitationChance)")
}

Custom Date Range

自定义日期范围

Request forecasts for specific date ranges using
WeatherQuery
.
swift
func 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
}
使用
WeatherQuery
请求指定日期范围的预报。
swift
func 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
}

Weather Alerts

天气警报

Fetch active weather alerts for a location. Alerts include severity, summary, and affected regions.
swift
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
        }
    }
}
获取指定位置的活跃天气警报。警报包含严重程度、摘要和受影响区域信息。
swift
func fetchAlerts(for location: CLLocation) async throws -> [WeatherAlert]? {
    let weather = try await weatherService.weather(for: location)
    return weather.weatherAlerts
}

// 处理警报
if let alerts = weatherAlerts {
    for alert in alerts {
        print("警报: \(alert.summary)")
        print("严重程度: \(alert.severity)")
        print("受影响区域: \(alert.region)")
        if let detailsURL = alert.detailsURL {
            // 跳转到完整警报详情页面
        }
    }
}

Selective Queries

选择性查询

Fetch only the datasets you need to minimize API usage and response size. Each
WeatherQuery
type maps to one dataset.
仅获取你需要的数据集,以减少API调用量和响应大小。每个
WeatherQuery
类型对应一个数据集。

Single Dataset

单个数据集

swift
let current = try await weatherService.weather(
    for: location,
    including: .current
)
// current is CurrentWeather
swift
let current = try await weatherService.weather(
    for: location,
    including: .current
)
// current为CurrentWeather类型

Multiple Datasets

多个数据集

swift
let (current, hourly, daily) = try await weatherService.weather(
    for: location,
    including: .current, .hourly, .daily
)
// current: CurrentWeather, hourly: Forecast<HourWeather>, daily: Forecast<DayWeather>
swift
let (current, hourly, daily) = try await weatherService.weather(
    for: location,
    including: .current, .hourly, .daily
)
// current: CurrentWeather, hourly: Forecast<HourWeather>, daily: Forecast<DayWeather>

Minute Forecast

分钟级预报

Available in limited regions. Returns precipitation forecasts at minute granularity for the next hour.
swift
let minuteForecast = try await weatherService.weather(
    for: location,
    including: .minute
)
// minuteForecast: Forecast<MinuteWeather>?  (nil if unavailable)
仅在部分地区可用。返回未来一小时内分钟级的降水预报。
swift
let minuteForecast = try await weatherService.weather(
    for: location,
    including: .minute
)
// minuteForecast: Forecast<MinuteWeather>?  (不可用时返回nil)

Available Query Types

可用查询类型

QueryReturn TypeDescription
.current
CurrentWeather
Current observed conditions
.hourly
Forecast<HourWeather>
25 hours from current hour
.daily
Forecast<DayWeather>
10 days from today
.minute
Forecast<MinuteWeather>?
Next-hour precipitation (limited regions)
.alerts
[WeatherAlert]?
Active weather alerts
.availability
WeatherAvailability
Dataset availability for location
查询类型返回类型描述
.current
CurrentWeather
当前观测到的天气状况
.hourly
Forecast<HourWeather>
从当前时间开始的25小时预报
.daily
Forecast<DayWeather>
从今日开始的10天预报
.minute
Forecast<MinuteWeather>?
未来一小时的分钟级降水预报(仅部分地区可用)
.alerts
[WeatherAlert]?
活跃天气警报
.availability
WeatherAvailability
指定位置的数据集可用性

Attribution

版权声明

Apple requires apps using WeatherKit to display attribution. This is a legal requirement.
Apple要求使用WeatherKit的应用必须展示版权声明,这是一项法律要求。

Fetching Attribution

获取版权声明信息

swift
func fetchAttribution() async throws -> WeatherAttribution {
    return try await weatherService.attribution
}
swift
func fetchAttribution() async throws -> WeatherAttribution {
    return try await weatherService.attribution
}

Displaying Attribution in SwiftUI

在SwiftUI中展示版权声明

swift
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
    }
}
swift
import SwiftUI
import WeatherKit

struct WeatherAttributionView: View {
    let attribution: WeatherAttribution
    @Environment(\.colorScheme) private var colorScheme

    var body: some View {
        VStack(spacing: 8) {
            // 展示Apple Weather标识
            AsyncImage(url: markURL) { image in
                image
                    .resizable()
                    .scaledToFit()
                    .frame(height: 20)
            } placeholder: {
                EmptyView()
            }

            // 链接到法律声明页面
            Link("天气数据来源", destination: attribution.legalPageURL)
                .font(.caption2)
                .foregroundStyle(.secondary)
        }
    }

    private var markURL: URL {
        colorScheme == .dark
            ? attribution.combinedMarkDarkURL
            : attribution.combinedMarkLightURL
    }
}

Attribution Properties

版权声明属性

PropertyUse
combinedMarkLightURL
Apple Weather mark for light backgrounds
combinedMarkDarkURL
Apple Weather mark for dark backgrounds
squareMarkURL
Square Apple Weather logo
legalPageURL
URL to the legal attribution web page
legalAttributionText
Text alternative when a web view is not feasible
serviceName
Weather data provider name
属性用途
combinedMarkLightURL
浅色背景下的Apple Weather标识
combinedMarkDarkURL
深色背景下的Apple Weather标识
squareMarkURL
方形Apple Weather图标
legalPageURL
法律声明页面的链接
legalAttributionText
无法使用网页视图时的文本替代方案
serviceName
天气数据提供商名称

Availability

可用性

Check which weather datasets are available for a given location. Not all datasets are available in all countries.
swift
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
    }
}
检查指定位置支持哪些天气数据集。并非所有数据集在所有国家都可用。
swift
func checkAvailability(for location: CLLocation) async throws {
    let availability = try await weatherService.weather(
        for: location,
        including: .availability
    )

    // 检查特定数据集的可用性
    if availability.alertAvailability == .available {
        // 可以安全获取警报
    }

    if availability.minuteAvailability == .available {
        // 该地区支持分钟级预报
    }
}

Common Mistakes

常见错误

DON'T: Ship without Apple Weather attribution

错误做法:发布时未添加Apple Weather版权声明

Omitting attribution violates the WeatherKit terms of service and risks App Review rejection.
swift
// WRONG: Show weather data without attribution
VStack {
    Text("72F, Sunny")
}

// CORRECT: Always include attribution
VStack {
    Text("72F, Sunny")
    WeatherAttributionView(attribution: attribution)
}
省略版权声明违反WeatherKit服务条款,可能导致应用审核被拒。
swift
// 错误示例:展示天气数据但未添加版权声明
VStack {
    Text("72°F, 晴天")
}

// 正确示例:始终包含版权声明
VStack {
    Text("72°F, 晴天")
    WeatherAttributionView(attribution: attribution)
}

DON'T: Fetch all datasets when you only need current conditions

错误做法:仅需当前状况时却获取所有数据集

Each dataset query counts against your API quota. Fetch only what you display.
swift
// 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.temperature
每个数据集的查询都会计入API配额。仅获取你需要展示的数据。
swift
// 错误示例:获取所有数据
let weather = try await weatherService.weather(for: location)
let temp = weather.currentWeather.temperature

// 正确示例:仅获取当前天气状况
let current = try await weatherService.weather(
    for: location,
    including: .current
)
let temp = current.temperature

DON'T: Ignore minute forecast unavailability

错误做法:忽略分钟级预报不可用的情况

Minute forecasts return
nil
in unsupported regions. Force-unwrapping crashes.
swift
// 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
}
在不支持的地区,分钟级预报会返回nil。强制解包会导致崩溃。
swift
// 错误示例:强制解包分钟级预报
let minutes = try await weatherService.weather(for: location, including: .minute)
for m in minutes! { ... } // 在不支持的地区会崩溃

// 正确示例:处理nil情况
if let minutes = try await weatherService.weather(for: location, including: .minute) {
    for m in minutes { ... }
} else {
    // 该地区不支持分钟级预报
}

DON'T: Forget the WeatherKit entitlement

错误做法:忘记添加WeatherKit权限

Without the capability enabled,
WeatherService
calls throw at runtime.
swift
// 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
未启用该功能时,
WeatherService
调用会在运行时抛出错误。
swift
// 错误示例:未配置WeatherKit功能
let weather = try await weatherService.weather(for: location) // 抛出错误

// 正确示例:在Xcode的签名与功能中启用WeatherKit
// 并在Apple开发者后台为你的App ID启用该功能

DON'T: Make repeated requests without caching

错误做法:不缓存数据,重复请求

Weather data updates every few minutes, not every second. Cache responses to stay within API quotas and improve performance.
swift
// 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
    }
}
天气数据每几分钟更新一次,并非每秒更新。缓存响应以避免超出API配额并提升性能。
swift
// 错误示例:每次视图出现时都获取数据
.task {
    let weather = try? await fetchWeather()
}

// 正确示例:设置缓存过期时间
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
    }
}

Review Checklist

审核检查清单

  • WeatherKit capability enabled in Xcode and Apple Developer portal
  • Active Apple Developer Program membership (required for WeatherKit)
  • Apple Weather attribution displayed wherever weather data appears
  • Attribution mark uses correct color scheme variant (light/dark)
  • Legal attribution page linked or
    legalAttributionText
    displayed
  • Only needed
    WeatherQuery
    datasets fetched (not full
    weather(for:)
    when unnecessary)
  • Minute forecast handled as optional (nil in unsupported regions)
  • Weather alerts checked for nil before iteration
  • Responses cached with a reasonable staleness interval (5-15 minutes)
  • WeatherAvailability
    checked before fetching region-limited datasets
  • Location permission requested before passing
    CLLocation
    to service
  • Temperature and measurements formatted with
    Measurement.formatted()
    for locale
  • 已在Xcode和Apple开发者后台启用WeatherKit功能
  • 拥有有效的Apple开发者计划会员资格(WeatherKit必需)
  • 所有展示天气数据的位置都添加了Apple Weather版权声明
  • 版权声明标识使用了适配当前配色方案的版本(浅色/深色)
  • 已链接到法律声明页面或展示了
    legalAttributionText
  • 仅获取所需的
    WeatherQuery
    数据集(无需时不调用完整的
    weather(for:)
  • 已将分钟级预报作为可选类型处理(不支持地区返回nil)
  • 遍历天气警报前已检查是否为nil
  • 已为响应设置合理的缓存过期时间(5-15分钟)
  • 获取地区受限的数据集前已检查
    WeatherAvailability
  • 向服务传递
    CLLocation
    前已请求位置权限
  • 温度和测量值已使用
    Measurement.formatted()
    适配本地化

References

参考资料