Tutorial: Integrating WeatherKit and Core Location in a SwiftUI App

With iOS 16, WeatherKit and Core Location makes it easy to access weather data within a SwiftUI App.

In this tutorial, we will walk through how to set up a SwiftUI app for iOS that integrates with WeatherKit and Core Location to display the weather at a user’s current location.

Setting up a SwiftUI App

First, we will be setting up a SwiftUI app using the “App” template on XCode:

Xcode template selection screen, with “App” selected.

I’m using SwiftUI for the Interface and Swift for the language:

Options for the Xcode “App” template, with SwiftUI selected as the interface and Swift selected as the language.

When your template is set up, it should look like this:

The Xcode template code when you open a new “App” template project.

Setting Up WeatherKit

Before we add any code to our project, there are a couple of steps we need to take first to authorize our app to access WeatherKit data.

Capability Setup

Head over to developer.apple.com/account:

The developer.apple.com/account screen

We’ll be going to the “Certificates, Identifiers & Profiles” section on the left.

The Certificates page on the developer.apple.com website.

Switch to the “Identifiers” tab and press the blue “+” button to add a new identifier

The Identifiers page on the developer.apple.com website.

We will be registering a new App ID:

The “Register a new identifier” page on the developer.apple.com website with “App ID” selected.

Select the “App” type from the following screen:

“App” selected as the type on the “Register a new identifier” page.

You should now be brought to the “Register an App ID” screen:

The full app registration page for the “Register an App ID” screen.

We need to enable WeatherKit in 2 locations in this Menu. First, on the Capabilities tab, scroll down to the bottom and check the “WeatherKit” box:

WeatherKit checkbox selected on the “Capabilities” tab on the “Register an app ID” screen.

Second, scroll back to the top and switch to the “App Services” tab and check the “WeatherKit” box here

WeatherKit checkbox selected on the “App Services” tab of the “Register an App ID” screen

Finally, add a description and Bundle ID. The important element is to make sure the Bundle ID matches the “Bundle Identifier” in Xcode in the “Signing & Capabilities” screen. Xcode automatically generates a Bundle Identifier, so I’ve just copied this from Xcode and pasted on the webpage.

Xcode “Signing & Capabilities” tab, with the Bundle ID text selected

The final step on this site is to press the “Register” button to register the App ID on the site:

The confirmation screen on the “Register an App ID” page.

Once that’s all set up, switch back to Xcode on the “Signing & Capability” screen, and press the “+ Capability” button:

Xcode “Signing and Capabilities” screen with a red arrow pointing to the “+ Capability” button

This will prompt a pop up of different Capability options you could add to your app:

The capability pop up window

Scroll down or start typing to find the “WeatherKit” capability:

The capability pop up window with WeatherKit selected

Select this to add this Capability to your app:

The “Signing & Capabilities” tab after WeatherKit has been added as a capability

Now we’re all set up with the WeatherKit Capability! Note that it might take around 30 minutes for the servers to process this change before we are able to access this data, but wait times can be shorter in my experience. While we wait, we can turn to the code to start integrating WeatherKit into our app.

WeatherKit Manager

We are going to be creating a “WeatherKitManager” Class where we will handle everything related to WeatherKit. Add a new Swift file to your project:

New file creation screen in Xcode, with “swift” selected.

In order to access WeatherKit, import WeatherKit into this file:

import WeatherKit

As our WeatherKit code will be using async / await, we will be marking our class with @MainActor (for more information on @MainActor and async / await, check out this Hacking With Swift article on @MainActor and this Hacking With Swift article on async / await). Because we will want our SwiftUI views to update when the weather data gets updated, this class will conform to the ObservableObject protocol:

@MainActor class WeatherKitManager: ObservableObject { }

We will then setup a “weather” variable what we will update when we get new weather data. This variable will be of WeatherKit’s “Weather” struct. This will be optional, as when the user first opens the app, the async / await code will need a moment to load, so our data will be empty while it loads. And finally, this will be @Published as we will want our SwiftUI view to update when there are changes to this variable:

@Published var weather: Weather?

The core of this Class will be our “getWeather()” function, which is what will be making the call to Apple’s servers and actually getting the Weather data. As mentioned above, this will be an async / await call, which is setting our “weather” variable to the contents of our WeatherService.shared.weather(for: .init()) request (Apple Documentation). I’m using the lattitude and longitude of Apple Park for now, but we will be updating this with our Core Location data later in this tutorial.

For the purposes of this tutorial, I’m going to add 2 more variables to this Class to make the data easier to integrate into our SwiftUI view. The .currentWeather struct on our Weather data has a lot of functionality I encourage you to explore, but I will be specifically grabbing the “.symbolName” and “.temperature” properties for our App.

weather?.currentWeather.symbolName is great, because it automatically provides an SF Symbol icon that corresponds to the current weather at our selected location:

var symbol: String {
weather?.currentWeather.symbolName ?? "xmark"
}

weather?.currentWeather.temperature will provide us the current temperature at our selected location (which I am translating into fahrenheit):

var temp: String {
let temp =
weather?.currentWeather.temperature

let convert = temp?.converted(to: .fahrenheit).description
return convert ?? "Loading Weather Data"

}

All together, your WeatherKitManager class should look like this:

Setting Up The SwiftUI View

We will be modifying the ContentView code to a simple SwiftUI View that will show our weather at our selected location (Apple Park).

First, add an instance of our WeatherKitManager class as an @ObservedObject variable, so that we can access our weather data and have our view update when the data is updated:

@ObservedObject var weatherKitManager = WeatherKitManager()

I’m going to replace the template VStack with a Label that uses the temp and symbol strings we calculate in the “WeatherKitManager” Class:

Label(weatherKitManager.temp, systemImage: weatherKitManager.symbol)

Finally, we need to actually call our .getWeather function inside the “WeatherKitManager” Class to get the weather data. As this is an async / await function, we will place this inside a .task { }:

.task {await weatherKitManager.getWeather()}

Your ContentView should now look like this:

If you run this code on your iOS device, you should see this screen (the “Loading Weather Data” text while the async function is running, then the actual forecast when the data is successfully loaded):

Screenshots of the current code. On the left, the weather data is still loading, so the text reads “Loading Weather Data”. On the right, the weather data has loaded and is populating the text view with the SF symbol for the sun next to it in the Label.

Ta Da!

We now have successfully integrated WeatherKit into a SwiftUI App! Now, it’s time to integrate CoreLocation into this code so that a user can get the location data from their actual location (not just Apple Park).

Adding Core Location

I recommend you check out my tutorial on integrating Core Location into a SwiftUI project, but some of the code will be slightly changed as we incorporate this into our WeatherKit project:

Adding Info.plist entry

As we will be access the user’s location data, which is sensitive data, we will need to add an entry to the Info.plist file. We will be using the “Location When In Use” data from Core Location, so add a new item with the “Privacy — Location When In Use Usage Description” key and add a string about why we will be accessing the user’s location data. This will appear on the privacy prompt the user receives:

Info.plist screen in Xcode, added the “Privacy — Location When In Use Usage Description” key and description text

Adding in LocationDataManager

Add a new Swift file to the project that will contain our “LocationDataManager” Class:

Add new file screen in Xcode

We will be adding in the LocationDataManager code from my Core Location tutorial here, with two small additions. We will add in two computed variables for the latitude and longitude, so that it’s easier to work with when we integrate with our WeatherKit data:

var latitude: Double {
locationManager.location?.coordinate.latitude ?? 0.0
}
var longitude: Double {
locationManager.location?.coordinate.longitude ?? 0.0
}

The final LocationDataManager code should look like this:

Update to the WeatherKitManager File

As we will need our WeatherKit data to be dynamic and not just show the weather data at Apple Park, we will need to update our getWeather function to have inputs for latitude and longitude. Add inputs to the parentheses of the function for two double variables, and use these in the .weather(for:) code in place of our hard coded coordinates:

The final updated WeatherKitManager file should look like this:

Update to the ContentView

We now need to integrate everything together in the ContentView. First, we will be adding in our locationDataManager class so that we can access the Core Location data:

@StateObject var locationDataManager = LocationDataManager()

Next, we will be adding in an if statement to check to make sure we are authorized to access the location data before we try to call the weather data, otherwise the WeatherKit data will fall back on the default coordinates we’ve set up of 0.0 for latitude and longitude:

if locationDataManager.authorizationStatus == .authorizedWhenInUse {} else {
Text("Error Loading Location")
}

Finally, we will update our .getWeather() call in our .task{ } to receive the inputs of the latitude and longitude from our LocationDataManager:

Label(weatherKitManager.temp, systemImage: weatherKitManager.symbol)
.task {
await weatherKitManager.getWeather(latitude: locationDataManager.latitude, longitude: locationDataManager.longitude)
}

All together, the final ContentView should look like this:

When you run your code, you should see that the App now asks for permission for your location, and then loads the current temperature where you are!

The final code running on an iOS device. The Location Privacy pop up appears in the first screenshot. In the second screenshot, the “Loading Weather Data” text appears on screen. In the final screenshot, the weather data at the user’s location appears on screen with the matching SF Symbol in the Label.

I hope that this tutorial was helpful, and can get you started on your WeatherKit, Core Location and SwiftUI journey!

You can check out the full Github repository for this project here:

Here is some more Apple Documentation I would recommend checking out regarding WeatherKit:

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store