WeatherKit Tutorial: Getting Started
The tutorial covers exploring WeatherKit, displaying local weather forecasts and using Swift Charts for detailed predictions across locations. By Saleh Albuga.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
WeatherKit Tutorial: Getting Started
25 mins
- Getting Started
- Setting Up Your Project
- Registering App Identifiers
- Activating WeatherKit Capability
- Using WeatherService
- Displaying the Current Weather Forecast
- Exploring the Weather Data
- Getting a More Detailed Forecast
- 10-Day Weather Overview
- Presenting the Hourly Overview
- Adding a Daily Temperature Chart
- Adding an Hourly Temperature Chart
- Preparing the App for Distribution
- Where to Go From Here?
Many iOS apps use weather data as a supplementary feature in news apps or as crucial information that the app’s functionality hinges on, such as in planning or travel.
In 2020, Apple bought the Dark Sky weather app to enhance its macOS and iOS weather apps. Apple introduced WeatherKit at WWDC22, a framework for gathering weather data without relying on APIs or third-party SDKs.
If you choose to use a third-party API, it’s important to consider the extra factors involved, such as comprehending and creating a model for the response structure. If there isn’t a particular reason to get the information from another source, WeatherKit is the recommended choice.
In this tutorial, you’ll:
- Discover WeatherKit and the information it offers.
- Retrieve and show the weather forecast for your current location.
- Use Swift Charts to plot detailed weather predictions for various locations.
You should already know Swift, iOS and Xcode basics for this tutorial.
Also, have an Apple Developer account to set up an App ID with the WeatherKit App Service.
Getting Started
Download the starter project by clicking the Download Materials button at the top or bottom of the tutorial. Open the project and build and run.
KodecoWeather is a weather app with two tabs:
- Current: Which will show the current forecast for your location.
- Detailed: Will offer a detailed forecast for a list of locations, including hourly and daily weather predictions.
Setting Up Your Project
To use WeatherKit, follow these initial steps to enable it in your project. You’ll first need to register a new App Identifier with a specific Bundle ID for activation.
Registering App Identifiers
Visit the Apple developer portal and sign in with your Apple ID. Select Identifiers under the Certificates, IDs & Profiles category. Click the “+” icon near Identifiers. For the next two steps, click Continue, maintaining the default options for App ID and App.
On the Register an App ID page, input an Explicit Bundle ID, such as com.[yourName].KodecoWeather, then provide a brief description.
Activating WeatherKit Capability
WeatherKit, like ShazamKit or iCloud, is an app service and feature that requires activation. On the Register an App ID page, select the App Services tab, then check the box next to WeatherKit. Click Continue to complete registration.
In Xcode, open your starter project and access the Project Editor. Within Signing & Capabilities, ensure Automatically manage signing is checked, then enter the Bundle ID you specified earlier into Bundle identifier. Build and run.
In the upcoming section, you’ll begin working with WeatherKit.
Using WeatherService
Open WeatherData.swift, noticing the four methods in the WeatherData
class. Notice the following:
func currentWeather(for location: CLLocation) async -> CurrentWeather? {
let currentWeather = await Task.detached(priority: .userInitiated) {
let forecast = try? await self.service.weather(
for: location,
including: .current)
return forecast
}.value
return currentWeather
}
This code takes one parameter of type CLLocation
and returns a CurrentWeather
type struct, which contains the current weather data for that location. It calls the WeatherService
method of WeatherKit named weather(for:including:)
, which takes two parameters:
- A
CLLocation
, for which the weather forecast is retrieved. - A
WeatherQuery
, which specifies the forecast time. Here,.current
is passed to get the current forecast.
The following two methods, dailyForecast(for:)
and hourlyForecast(for:)
, are like the first method. But different forecasts are queried from the WeatherService
using .daily
and .hourly
, respectively.
WeatherKit provides WeatherService.weather(for:including:)
as the primary method for data requests. You can use many overloads to request up to five weather queries for a location in one request. For instance, you could write:
let (current, daily, hourly) = try await service.weather(for: location, including: .current, .daily, .hourly)
This query requests the current, daily and hourly forecasts at the same time. For simplicity, this tutorial uses one weather query per call.
The following section discusses the display of the current forecast for your location.
Displaying the Current Weather Forecast
Now, you’ll implement the app’s first section, which will:
- Obtain the user’s location.
- Query the WeatherService for that location.
- Display the desired weather measurements from the response.
First, open CurrentWeatherView.swift in the Views folder. Notice the first three variable definitions:
-
locationManager
: An instance of theLocationManager
helper class. This requests your location fromCoreLocation
. -
weatherServiceHelper
: Initialized with the singleton ofWeatherData
. This is the helper class observed in the previous section. -
currentWeather
: A state variable where theCurrentWeather
data from WeatherKit is stored.
Time to start coding. First you need to define a method that LocationManager
should call after obtaining a location. Add the following below the body
view:
func locationUpdated(location: CLLocation?, error: Error?) {
if let currentLocation: CLLocation = location, error == nil {
Task.detached {
isLoading = false
currentWeather = await weatherServiceHelper.currentWeather(for: currentLocation)
stateText = ""
}
} else {
stateText = "Cannot get your location. \n \(error?.localizedDescription ?? "")"
isLoading = false
}
}
This code first checks that a location is returned without error. It then:
- Sets
isLoading
to false to hide the ProgressView. - Calls the
currentWeather(for:)
method ofWeatherServiceHelper
, passing the location. Once execution completes, the response of typeCurrentWeather
is assigned to the state variable. - Then,
stateText
is set to remove any previously set “loading” or error text. - If a valid location is not retrieved, the error message is set in
stateText
.
To start the LocationManager
, add the following lines inside the View’s onAppear
closure:
isLoading = true
self.locationManager.updateLocation(handler: locationUpdated)
Here, you set isLoading
to true, which causes the ProgressView to be displayed. updateLocation(handler:)
is then called, passing the handler method that you added earlier.
Lastly, the retrieved forecast should be displayed to the user. Immediately below these lines in the VStack block:
if isLoading {
ProgressView()
}
Add the following:
if let current = currentWeather {
Image(systemName: current.symbolName)
.font(.system(size: 75.0, weight: .bold))
Text(current.condition.description)
.font(Font.system(.largeTitle))
let tUnit = current.temperature.unit.symbol
Text("\(current.temperature.value.formatted(.number.precision(.fractionLength(1))))\(tUnit)")
.font(Font.system(.title))
Spacer()
VStack(alignment: .leading) {
Text("Feels like: \(current.apparentTemperature.value.formatted(.number.precision(.fractionLength(1)))) \(tUnit)")
.font(Font.system(.title2))
Text("Humidity: \((current.humidity * 100).formatted(.number.precision(.fractionLength(1))))%")
.font(Font.system(.title2))
Text("Wind Speed: \(Int(current.wind.speed.value)), \(current.wind.compassDirection.description)")
.font(Font.system(.title2))
Text("UV Index: \(current.uvIndex.value)")
.font(Font.system(.title2))
}
Spacer()
Divider()
} else {
Text(stateText)
}
Here, you present many of the forecast parameters returned in currentWeather
. Build and run to see the results.
Note: If it’s been less than 30 minutes since you registered the App ID, WeatherKit requests won’t work. You’ll see the following authentication error in the console:
Grab a coffee or snack!
Note: If it’s been less than 30 minutes since you registered the App ID, WeatherKit requests won’t work. You’ll see the following authentication error in the console:
[AuthService] Failed to generate jwt token for com.apple.weatherkit.authservice with error: Error Domain=WeatherDaemon.WDSJWTAuthenticatorServiceListener.Errors Code=2 "(null)"
Grab a coffee or snack!
[AuthService] Failed to generate jwt token for com.apple.weatherkit.authservice with error: Error Domain=WeatherDaemon.WDSJWTAuthenticatorServiceListener.Errors Code=2 "(null)"
In the next section, you’ll explore the forecast data WeatherKit returns.