New SwiftUI Support for MapKit in Xcode 15
At WWDC 2023, Apple announced big improvements to SwiftUI support for MapKit. Learn all about your new options for maps, markers, annotations, style and camera position. By Audrey Tam.
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
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
New SwiftUI Support for MapKit in Xcode 15
5 mins
At WWDC 2023, Apple announced better SwiftUI support for MapKit. MapKit is a huge API, so it’ll be a while before we see a fully native SwiftUI version. But it’s definitely getting easier to display an interactive map view in your app. The most widely-used feature — annotations — gets a big improvement, and the new map style modifier requires almost no work to add a ton of pizzazz to your app. Slowly but surely, MapKit is acquiring a more SwiftUI “touch and feel”.
On the downside, if you’ve written a lot of Map code in the past couple of years, you might need to rewrite some of it, as Xcode 15 replaces all the Xcode 12 Map initializers.
Getting Started
Click Download materials at the top or bottom of this article to download the starter project. Open it in Xcode 15 beta to see what you have to work with.
PublicArt
I wrote the first version of PublicArt in 2014 for my very first raywenderlich.com tutorial MapKit Tutorial: Getting Started. It was an update of Ray’s original MapKit tutorial, and Andrew Tetlaw updated it again in 2020.
In 2019, I adapted PublicArt for SwiftUI Tutorial: Navigation. Using Xcode 11, I had to create a struct MapView: UIViewRepresentable to display an MKMapView in a SwiftUI app.
Earlier this year, Josh Steele updated PublicArt to Xcode 14.2 for our SwiftUI Fundamentals course. This is the starter project for this article: It uses the SwiftUI Map introduced in Xcode 12.
For this article, the starter project has iOS Deployment set to iOS 17.
Xcode 12 Map
Open LocationMap in the code editor. There’s an MKCoordinateRegion @State property. You pass a binding to this into the Map initializer, along with an array of annotationItems, then display a MapMarker using an artwork item’s coordinate property. When the Map view appears, you set region.center and region.span:
@State var region = MKCoordinateRegion()
...
Map(coordinateRegion: $region, annotationItems: [artwork]) { artwork in
MapMarker(coordinate: artwork.coordinate)
}
.onAppear {
region.center = artwork.coordinate
region.span = MKCoordinateSpan(latitudeDelta: 0.02, longitudeDelta: 0.02)
}
Refresh the preview or build and run the app, and navigate to the map:

Xcode 15 Beta Map
This year’s Map deprecates all the Xcode 12 initializers. Instead of bindings to MKCoordinateRegion or MKMapRect, you create a Map with MapCameraBounds or MapCameraPosition.
You can create a MapCameraBounds value with an MKCoordinateRegion or MKMapRect value, plus minimum and maximum distances, or you can create one using only minimum and maximum distances.
Or you can create a Map using no parameters at all!
Map & Marker
In LocationMap, comment out @State var region and replace all the Map code — Map(...) { ... }.onAppear { ... } — with this:
Map {
Marker(artwork.title, coordinate: artwork.coordinate)
}
MapMarker is deprecated, replaced by Marker, where you display a label, which is a View.

You get the same balloon marker, but the map has zoomed right in to it. To show approximately the same region as the old region, initialize Map with bounds:
Map(bounds:
MapCameraBounds(minimumDistance: 4500,
maximumDistance: 4500))

Annotation
In addition to Marker, there’s a new Annotation structure:
Annotation(artwork.title,
coordinate: artwork.coordinate) {
Image(systemName: "person.bust")
.padding(6)
.foregroundStyle(.white)
.background(Color.blue)
}

An Annotation displays both a label View and a content View, so you can customize your map pins. For example, add these properties to Artwork:
var symbol: String {
switch discipline {
case "Monument", "Sculpture":
return "person.bust"
case "Mural":
return "paintpalette"
case "Plaque":
return "person.text.rectangle"
default:
return "mappin"
}
}
var background: Color {
switch discipline {
case "Monument", "Sculpture":
return .blue
case "Mural":
return .mint
case "Plaque":
return .orange
default:
return .red
}
}
Each artwork has a discipline property, and these new properties specify symbols and background colors for the most-populated disciplines.
Now, replace your Marker and Annotation code with:
Marker(artwork.title, systemImage: artwork.symbol,
coordinate: artwork.coordinate)
.tint(artwork.background)
Annotation(artwork.title,
coordinate: artwork.coordinate,
anchor: .topLeading) {
Image(systemName: artwork.symbol)
.padding(6)
.foregroundStyle(.white)
.background(artwork.background)
}
Like the deprecated MapMarker, Marker lets you specify tint.
To see these custom symbols and colors at work, change the index of the artData item in the preview to 1. This artwork is a mural, so you get mint-colored pins, and the annotation symbol is a paint palette:

You probably wouldn’t want to use both Marker and Annotation for the same location, but while they’re both there, see how you can adjust the position of the annotation’s anchor:
Annotation(artwork.title,
coordinate: artwork.coordinate,
anchor: .topLeading) // add this argument

The value of anchor can be top, bottom, leading, trailing or combinations, or you can specify a CGPoint with x and y coordinates.
Map Style
And now for one of the best new features: mapStyle. Add this modifier to Map after its closing brace:
.mapStyle(.imagery(elevation: .realistic))
And change bounds to zoom in:
Map(bounds:
MapCameraBounds(minimumDistance: 1500,
maximumDistance: 1500))

Shift-Option-drag to move the map camera angle:

You can also add an initialPosition parameter to Map to set the map camera:
Map(
initialPosition: .camera(MapCamera(
centerCoordinate: artwork.coordinate,
distance: 1200,
heading: 90,
pitch: 60)),
bounds:
MapCameraBounds(minimumDistance: 1500,
maximumDistance: 1500)) {

Now, you can take an aerial tour around Honolulu by changing the artData item in the preview. For example, artData[12]:

There are only 17 artworks, so don’t go beyond artData[16].
Where to Go From Here?
Download the final project using Download materials at the top or bottom of this article.
In this article, you learned about:
- The new way to create a
Map. - The new
MarkerandAnnotationstructures. - The new
MapStylestructure. - The new
MapCamerastructure.
Meet MapKit for SwiftUI shows off several nifty features, including:
-
onMapCameraChange(frequency:), an instance method ofMapPitchButton. -
MapPolylineandMKRoute. -
MapCompassandMapScaleView.
We hope you enjoyed this tutorial, and if you have any questions or comments, please join the forum discussion below!