-
Notifications
You must be signed in to change notification settings - Fork 3
WeatherKit
Package
SwiftBindings.Apple.WeatherKit· Version26.2.6Auto-published fromapple-frameworks/WeatherKit/WEATHERKIT-GUIDE.md.
SwiftBindings.Apple.WeatherKit exposes Apple's WeatherKit framework to C# through .NET 10's native Swift interop — current conditions, hourly and daily forecasts, severe-weather alerts, sun/moon events, and the required Apple attribution. These are direct Swift calls, not Objective-C proxy wrappers. This guide maps the Swift workflow to the generated C# surface and documents exactly what is and isn't bound today.
- Requirements & install
- Naming conventions
- Quick start: fetch the weather for a location
- The
WeatherService - The
Weatherbundle - Current conditions
- Reading measurements
- Hourly & daily forecasts
- Weather conditions, precipitation & pressure
- Sun & moon events
- Wind & UV index
- Severe-weather alerts
- Availability & metadata
- Attribution (required by Apple)
- Errors
- Known limitations
- Memory & threading
- Reference links
- .NET 10.0+
- Target frameworks:
net10.0-ios26.2,net10.0-macos26.2,net10.0-maccatalyst26.2,net10.0-tvos26.2 - iOS 26.2+, macOS 26.2+, Mac Catalyst 26.2+, tvOS 26.2+
- macOS host for development
- A paid Apple Developer Program membership with the WeatherKit capability enabled. WeatherKit data is metered and entitlement-gated; calls fail without a provisioned app. The bindings themselves load and dispatch on the simulator, but live data requires the entitlement.
dotnet add package SwiftBindings.Apple.WeatherKit
using WeatherKit;WeatherKit uses Foundation
Measurement<Unit…>values andCoreLocation.CLLocation, so you'll typically also wantusing CoreLocation;. Both are pulled in transitively by the SwiftBindings runtime.
The generator applies a few consistent transforms over the Swift names. Knowing them makes the whole surface predictable:
| Swift | C# | Rule |
|---|---|---|
func weather(for:) async throws |
WeatherAsync(CLLocation, CancellationToken) |
async methods gain an Async suffix, return Task<T>, drop the first argument label, and take a trailing defaulted CancellationToken
|
WeatherService.shared |
WeatherService.Shared |
static singletons are PascalCase properties |
current.dewPoint |
current.DewPoint |
properties are PascalCase |
enum WeatherCondition { case clear } (RawRepresentable + descriptions) |
WeatherCondition class with static singletons (WeatherCondition.Clear), a .Tag (CaseTag), .Description, .RawValue, and .AllCases
|
rich Swift enums that carry behavior project to a class, not a C# enum
|
enum MoonPhase: Int |
C# enum MoonPhase : int + MoonPhaseExtensions
|
plain integer-backed enums stay C# enums; helper methods (GetDescription, ToRawValue, AllCases) live on a sibling …Extensions static class |
var hourlyForecast: Forecast<HourWeather> |
Forecast<HourWeather> implementing IReadOnlyList<HourWeather>
|
Forecast<T> projects to a standard read-only list (.Count, indexer, foreach) |
var weatherAlerts: [WeatherAlert]? |
IReadOnlyList<WeatherAlert>? |
optional Swift arrays project to a nullable IReadOnlyList<>
|
var highTemperatureTime: Date? |
DateTimeOffset? |
optional Date → nullable DateTimeOffset
|
using WeatherKit;
using CoreLocation;
// 1. Get the singleton service (Swift's WeatherService.shared)
var service = WeatherService.Shared;
// 2. Fetch the full weather bundle for a coordinate
using var location = new CLLocation(37.3349, -122.0090); // Apple Park
WeatherKit.Weather weather = await service.WeatherAsync(location);
// 3. Read current conditions
CurrentWeather now = weather.CurrentWeather;
Console.WriteLine($"{now.Condition.Description}"); // e.g. "Clear"
Console.WriteLine($"{now.Temperature.Value}°"); // value in the stored unit (Celsius)
Console.WriteLine($"Humidity: {now.Humidity:P0}"); // 0.0–1.0 fraction
// 4. Walk the daily forecast — Forecast<T> is an IReadOnlyList<T>
foreach (DayWeather day in weather.DailyForecast)
{
Console.WriteLine($"{day.Date:d}: {day.Condition.Description}, " +
$"hi {day.HighTemperature.Value}° / lo {day.LowTemperature.Value}°");
}WeatherAsync throws when the app lacks a WeatherKit entitlement or on network failure — see Errors.
WeatherService is the single entry point. It's a Swift class (reference type), so it does not need disposing for correctness.
| Member | Signature | Notes |
|---|---|---|
WeatherService.Shared |
static WeatherService |
the shared singleton — use this |
new WeatherService() |
constructor | also available if you prefer your own instance |
WeatherAsync(...) |
Task<Weather> WeatherAsync(CLLocation location, CancellationToken ct = default) |
fetches everything for a location |
GetAttributionAsync() |
Task<WeatherAttribution> GetAttributionAsync(CancellationToken ct = default) |
the legally-required attribution metadata |
var service = WeatherService.Shared;
WeatherKit.Weather weather = await service.WeatherAsync(location);The variadic, query-based overloads of Swift's
weather(for:including:)(the ones that fetch just.current,.hourly,.daily, etc. viaWeatherQuery<T>) are not bound in this release — only the all-in-oneWeatherAsync(CLLocation)is callable. See Known limitations. To get a single dataset, fetch the fullWeatherand read the property you need.
WeatherAsync returns a Weather value aggregating every dataset:
| Property | Type | Notes |
|---|---|---|
CurrentWeather |
CurrentWeather |
conditions right now |
MinuteForecast |
Forecast<MinuteWeather>? |
next-hour minute-by-minute precipitation; null where unavailable |
HourlyForecast |
Forecast<HourWeather> |
hour-by-hour |
DailyForecast |
Forecast<DayWeather> |
day-by-day |
WeatherAlerts |
IReadOnlyList<WeatherAlert>? |
active severe-weather alerts; null where none |
Availability |
WeatherAvailability |
which datasets are supported at this location |
WeatherKit.Weather weather = await service.WeatherAsync(location);
CurrentWeather now = weather.CurrentWeather;
var hourly = weather.HourlyForecast; // Forecast<HourWeather>
var daily = weather.DailyForecast; // Forecast<DayWeather>
var minute = weather.MinuteForecast; // Forecast<MinuteWeather>? (may be null)
var alerts = weather.WeatherAlerts; // IReadOnlyList<WeatherAlert>? (may be null)CurrentWeather carries the instantaneous reading:
| Property | Type |
|---|---|
Date |
DateTimeOffset |
Condition |
WeatherCondition |
SymbolName |
string (SF Symbol name) |
Temperature |
Measurement<NSUnitTemperature> |
ApparentTemperature |
Measurement<NSUnitTemperature> |
DewPoint |
Measurement<NSUnitTemperature> |
Humidity |
double (0.0–1.0) |
Pressure |
Measurement<NSUnitPressure> |
PressureTrend |
PressureTrend |
CloudCover |
double (0.0–1.0) |
CloudCoverByAltitude |
CloudCoverByAltitude |
IsDaylight |
bool |
PrecipitationIntensity |
Measurement<NSUnitSpeed> |
UvIndex |
UVIndex |
Visibility |
Measurement<NSUnitLength> |
Wind |
Wind |
Metadata |
WeatherMetadata |
CurrentWeather c = weather.CurrentWeather;
Console.WriteLine($"{c.Condition.Description} ({c.SymbolName})");
Console.WriteLine($"Temp {c.Temperature.Value}°, feels {c.ApparentTemperature.Value}°");
Console.WriteLine($"Daylight: {c.IsDaylight}, UV {c.UvIndex.Value} ({c.UvIndex.Category})");WeatherKit returns physical quantities as Foundation Measurement<TUnit> (e.g. Swift.Foundation.Measurement<Foundation.NSUnitTemperature>). The binding exposes a single member:
public double Value { get; } // magnitude in the measurement's stored unitWeatherKit stores values in SI/metric units (Celsius for temperature, meters for length, m/s for speed, hPa-class pressure). Read the raw magnitude with .Value and format/convert as your app requires:
double celsius = weather.CurrentWeather.Temperature.Value;
double fahrenheit = celsius * 9.0 / 5.0 + 32.0;
Measurement<T>implementsIDisposable. When you read several measurements off a forecast element in a tight loop, wrap them inusing(see Memory & threading).
Both forecast collections are Forecast<T>, which implements IReadOnlyList<T> — use .Count, the indexer, or foreach:
var hourly = weather.HourlyForecast; // Forecast<HourWeather>
HourWeather firstHour = hourly[0];
int hours = hourly.Count;
foreach (HourWeather h in hourly)
Console.WriteLine($"{h.Date:t}: {h.Temperature.Value}°, {h.PrecipitationChance:P0} precip");HourWeather properties: Date, Condition, SymbolName, Temperature, ApparentTemperature, DewPoint, Humidity, IsDaylight, CloudCover, CloudCoverByAltitude, Precipitation, PrecipitationChance (double), PrecipitationAmount (Measurement<NSUnitLength>), SnowfallAmount (Measurement<NSUnitLength>), Pressure, PressureTrend, UvIndex, Visibility, Wind.
DayWeather properties: Date, Condition, SymbolName, HighTemperature, HighTemperatureTime (DateTimeOffset?), LowTemperature, LowTemperatureTime (DateTimeOffset?), MaximumHumidity, MinimumHumidity, Precipitation, PrecipitationChance, PrecipitationAmount, RainfallAmount, SnowfallAmount, PrecipitationAmountByType, Sun (SunEvents), Moon (MoonEvents), UvIndex, MaximumVisibility / MinimumVisibility (double), Wind, HighWindSpeed (Measurement<NSUnitSpeed>?), DaytimeForecast / OvernightForecast (DayPartForecast), RestOfDayForecast (DayPartForecast?).
A DayPartForecast (day/night split) exposes: CloudCover, CloudCoverByAltitude, Condition, HighTemperature, LowTemperature, Precipitation, PrecipitationAmountByType, PrecipitationChance, MaximumHumidity, MinimumHumidity, MaximumVisibility, MinimumVisibility, Wind, HighWindSpeed.
DayWeather today = weather.DailyForecast[0];
DayPartForecast night = today.OvernightForecast;
Console.WriteLine($"Tonight: {night.Condition.Description}, low {night.LowTemperature.Value}°");
if (today.RestOfDayForecast is { } rest)
Console.WriteLine($"Rest of day: {rest.Condition.Description}");WeatherCondition is a projected rich Swift enum — a class with static singletons, not a C# enum. Discriminate with .Tag (a WeatherCondition.CaseTag), or read the localized text directly:
WeatherCondition cond = weather.CurrentWeather.Condition;
string text = cond.Description; // localized, e.g. "Partly Cloudy"
string a11y = cond.AccessibilityDescription; // VoiceOver string
string raw = cond.RawValue; // e.g. "partlyCloudy"
cond.ToString(); // same as Description
if (cond.Tag == WeatherCondition.CaseTag.Rain) { /* … */ }
// Singletons & lookups
WeatherCondition clear = WeatherCondition.Clear;
WeatherCondition? parsed = WeatherCondition.FromRawValue("snow");
IReadOnlyList<WeatherCondition> all = WeatherCondition.AllCases;Available singletons include Blizzard, BlowingDust, BlowingSnow, Breezy, Clear, Cloudy, Drizzle, Flurries, Foggy, FreezingDrizzle, FreezingRain, Frigid, Hail, Haze, HeavyRain, HeavySnow, Hot, Hurricane, IsolatedThunderstorms, MostlyClear, MostlyCloudy, PartlyCloudy, Rain, ScatteredThunderstorms, Sleet, Smoky, Snow, StrongStorms, SunFlurries, SunShowers, Thunderstorms, TropicalStorm, Windy, WintryMix.
Precipitation follows the same class-with-singletons shape: singletons None, Hail, Mixed, Rain, Sleet, Snow; members .Tag, .Description, .AccessibilityDescription, .RawValue, .AllCases, Precipitation.FromRawValue(...).
PressureTrend likewise: singletons Rising, Falling, Steady; members .Tag, .Description, .AccessibilityDescription, .RawValue, .AllCases, PressureTrend.FromRawValue(...).
DayWeather.Sun (SunEvents) — all DateTimeOffset? (null where the event doesn't occur, e.g. polar day/night):
AstronomicalDawn, NauticalDawn, CivilDawn, Sunrise, SolarNoon, Sunset, CivilDusk, NauticalDusk, AstronomicalDusk, SolarMidnight.
DayWeather.Moon (MoonEvents):
| Property | Type |
|---|---|
Phase |
MoonPhase |
Moonrise |
DateTimeOffset? |
Moonset |
DateTimeOffset? |
MoonPhase is a plain C# enum (New, WaxingCrescent, FirstQuarter, WaxingGibbous, Full, WaningGibbous, LastQuarter, WaningCrescent). Its helpers live on MoonPhaseExtensions:
MoonEvents moon = weather.DailyForecast[0].Moon;
string phase = moon.Phase.GetDescription(); // localized
string a11y = moon.Phase.GetAccessibilityDescription();
string symbol = moon.Phase.GetSymbolName(); // SF Symbol
string raw = moon.Phase.ToRawValue(); // "full"
MoonPhase? p = MoonPhaseExtensions.FromRawValue("full");
var allPhases = MoonPhaseExtensions.AllCases;
SunEvents sun = weather.DailyForecast[0].Sun;
if (sun.Sunrise is { } rise) { /* DateTimeOffset */ }Wind:
| Property | Type |
|---|---|
CompassDirection |
Wind.CompassDirectionType |
Direction |
Measurement<NSUnitAngle> |
Speed |
Measurement<NSUnitSpeed> |
Gust |
Measurement<NSUnitSpeed>? |
Wind.CompassDirectionType is a C# enum with all 16 points (North=0 … NorthNorthwest=15). Helpers live on WindCompassDirectionTypeExtensions:
Wind wind = weather.CurrentWeather.Wind;
Console.WriteLine($"{wind.Speed.Value} m/s from {wind.CompassDirection.GetDescription()}");
string abbr = wind.CompassDirection.GetAbbreviation(); // e.g. "NNW"
var allDirs = WindCompassDirectionTypeExtensions.AllCases; // 16 entriesUVIndex:
| Property | Type |
|---|---|
Value |
int |
Category |
UVIndex.ExposureCategory |
UVIndex.ExposureCategory is a C# enum (Low=0, Moderate, High, VeryHigh, Extreme=4) with helpers on UVIndexExposureCategoryExtensions:
UVIndex uv = weather.CurrentWeather.UvIndex;
Console.WriteLine($"UV {uv.Value}: {uv.Category.GetDescription()}");
var allCats = UVIndexExposureCategoryExtensions.AllCases;Weather.WeatherAlerts is a nullable IReadOnlyList<WeatherAlert>. Each WeatherAlert:
| Property | Type | Notes |
|---|---|---|
Summary |
string |
human-readable summary |
Source |
string |
issuing agency |
Region |
string? |
affected region |
Severity |
WeatherSeverity |
severity level |
DetailsURL |
Foundation.NSUrl |
link to full details |
Metadata |
WeatherMetadata |
data provenance |
if (weather.WeatherAlerts is { } alerts)
{
foreach (WeatherAlert alert in alerts)
{
Console.WriteLine($"[{alert.Severity.Description}] {alert.Summary} — {alert.Source}");
Console.WriteLine(alert.DetailsURL.AbsoluteString);
if (alert.Region is { } region) Console.WriteLine($"Region: {region}");
}
}WeatherSeverity is a projected rich enum (class): singletons Minor, Moderate, Severe, Extreme, Unknown; members .Tag, .Description, .AccessibilityDescription, .RawValue, .AllCases, WeatherSeverity.FromRawValue(...).
Weather.Availability (WeatherAvailability) tells you which datasets the location supports:
| Property | Type |
|---|---|
MinuteAvailability |
WeatherAvailability.AvailabilityKind |
AlertAvailability |
WeatherAvailability.AvailabilityKind |
AvailabilityKind is a projected rich enum (class): singletons Available, TemporarilyUnavailable, Unsupported, Unknown; members .Tag (CaseTag: Available=0, TemporarilyUnavailable, Unsupported, Unknown), .RawValue, AvailabilityKind.FromRawValue(...).
if (weather.Availability.MinuteAvailability.Tag ==
WeatherKit.WeatherAvailability.AvailabilityKind.CaseTag.Available)
{
var minute = weather.MinuteForecast; // safe to expect data
}WeatherMetadata (carried by CurrentWeather, WeatherAlert, etc.) describes data provenance:
| Property | Type |
|---|---|
Date |
DateTimeOffset (when the data is valid) |
ExpirationDate |
DateTimeOffset (cache it until this time) |
Location |
CoreLocation.CLLocation |
Apple's WeatherKit terms require you to display the Apple Weather logo and a link to the legal/data-source page. Fetch WeatherAttribution via the service:
WeatherAttribution attribution = await service.GetAttributionAsync();
string name = attribution.ServiceName; // "Apple Weather"
string text = attribution.LegalAttributionText;
Foundation.NSUrl legal = attribution.LegalPageURL;
Foundation.NSUrl squareMark = attribution.SquareMarkURL; // logo (square)
Foundation.NSUrl darkMark = attribution.CombinedMarkDarkURL; // logo for dark UI
Foundation.NSUrl lightMark = attribution.CombinedMarkLightURL; // logo for light UIDownload the appropriate mark image for your UI's appearance and link it to LegalPageURL.
WeatherAsync and GetAttributionAsync are async throws in Swift; the binding surfaces a thrown Swift error as a faulted Task. Awaiting rethrows it as a Swift.Runtime.SwiftException (or a typed SwiftException<TError>); a synchronous .Wait()/.Result wraps it in an AggregateException.
using Swift.Runtime;
try
{
var weather = await service.WeatherAsync(location);
}
catch (SwiftException ex)
{
// No WeatherKit entitlement, network failure, throttling, etc.
Console.WriteLine($"WeatherKit error: {ex.Message}");
}There is also a plain WeatherError enum (PermissionDenied=0, Unknown=1) with localized-text extension methods on WeatherErrorExtensions:
string? desc = WeatherError.PermissionDenied.GetErrorDescription();
string? reason = WeatherError.PermissionDenied.GetFailureReason();
string? help = WeatherError.PermissionDenied.GetRecoverySuggestion();Some Swift APIs are not yet emitted by the generator and are therefore not callable from C#. Don't try to use them — they don't exist on the binding:
-
Query-based
weather(for:including:)overloads. OnlyWeatherService.WeatherAsync(CLLocation)is bound. TheWeatherQuery<T>selectors (.current,.hourly,.daily,.alerts,.minute,.availability,.changes,.historicalComparisons) exist as types but the methods that consume them (the variadicweather(for:including:)family) are skipped (the generator reports "closure or async in generic type member" / "parameter or return type not yet supported"). Fetch the fullWeatherand read the property you need. -
Historical statistics & summaries. The result types (
DailyWeatherStatistics<T>,MonthlyWeatherStatistics<T>,HourlyWeatherStatistics<T>,DailyWeatherSummary<T>,DayTemperatureStatistics, etc.) are fully emitted and implementIReadOnlyList<T>. However, theWeatherServiceentry-point methods that produce them (dailyStatistics,monthlyStatistics,hourlyStatistics,dailySummary) are not bound — they use Swift variadic generic parameter packs (each .../repeat each ...) which have no C# equivalent. The types are accessible in the namespace but there is no callable service method to populate them. -
Codable round-tripping of nested rich enums. The synthesized
Codablemembers on several value types are pruned by design (they depend on unresolvable existentialEncoder/Decoderprotocols). Most value types still expose hand-rolledEncodeToJson()/DecodeFromJson(byte[])helpers if you need JSON, but the per-propertyencode/init(from:)Swift members are not bound.
The validated test app (apple-frameworks/WeatherKit/tests/Tests.cs) exercises the metadata, enums, singletons, Forecast<T> projection, and the GetAttributionAsync async bridge. Real-data fetches require a live WeatherKit entitlement and are not exercised in CI.
-
Most WeatherKit types wrap Swift structs and implement
IDisposable(Weather,CurrentWeather,DayWeather,HourWeather,WeatherAttribution,Wind,UVIndex,Forecast<T>,Measurement<T>, …). For short-lived locals the finalizer cleans up, butusing varis the recommended pattern for deterministic cleanup —Disposeis safe on every generated type and double-Dispose is a no-op.using var location = new CLLocation(lat, lon); using WeatherKit.Weather weather = await service.WeatherAsync(location); using var temp = weather.CurrentWeather.Temperature; double c = temp.Value;
-
WeatherServiceis a Swift class with automatic ARC bridging;Dispose()is available but not required. BothWeatherServiceandWeatherareSendable, so instances may be shared across .NET threads without external synchronization. -
Async hops.
WeatherAsync/GetAttributionAsyncbridge SwiftasynctoTask<T>; continuations resume on a thread-pool thread, so marshal back to your UI thread before touching UI. Both accept a trailingCancellationTokenthat cancels the in-flight Swift task.
-
ActivityKit —
SwiftBindings.Apple.ActivityKitv26.2.6 -
CryptoKit —
SwiftBindings.Apple.CryptoKitv26.2.6 -
FamilyControls —
SwiftBindings.Apple.FamilyControlsv26.2.6 -
LiveCommunicationKit —
SwiftBindings.Apple.LiveCommunicationKitv26.2.6 -
Matter —
SwiftBindings.Apple.Matterv26.2.6 -
MatterSupport —
SwiftBindings.Apple.MatterSupportv26.2.6 -
MusicKit —
SwiftBindings.Apple.MusicKitv26.2.6 -
ProximityReader —
SwiftBindings.Apple.ProximityReaderv26.2.6 -
RealityFoundation —
SwiftBindings.Apple.RealityFoundationv26.2.6 -
RealityKit —
SwiftBindings.Apple.RealityKitv26.2.6 -
RoomPlan —
SwiftBindings.Apple.RoomPlanv26.2.6 -
StoreKit2 —
SwiftBindings.Apple.StoreKit2v26.2.6 -
TipKit —
SwiftBindings.Apple.TipKitv26.2.6 -
Translation —
SwiftBindings.Apple.Translationv26.2.6 -
WeatherKit —
SwiftBindings.Apple.WeatherKitv26.2.6 -
WorkoutKit —
SwiftBindings.Apple.WorkoutKitv26.2.6