2025 Maps & Location
WWDC25 · 21 min · Maps & Location
Go further with MapKit
Discover the latest updates to MapKit and MapKit JS. We’ll introduce a new type of directions — cycling — and show you how to enable 3D Look Around imagery on the web. Learn how the new Geocoding API supports conversion between coordinates and addresses, and how to use the Address Representations API to get the most appropriate address for a region. Then we’ll wrap it up with a new way of referencing places that ensures your app will work seamlessly with App Intents.
Watch at developer.apple.com ↗Chapters
Code shown on screen · 19 snippets
Putting Marker on the Map with a coordinate
// Putting Marker on the Map with a coordinate
let annaLiviaCoordinates = CLLocationCoordinate2D(
latitude: 53.347673,
longitude: -6.290198
)
var body: some View {
Map {
Marker(
"Anna Livia Fountain",
coordinate: annaLiviaCoordinates
)
}
} Creating and resolving a PlaceDescriptor with coordinate PlaceRepresentation
// Creating and resolving a PlaceDescriptor with coordinate PlaceRepresentation
import GeoToolbox
import MapKit
let annaLiviaCoordinates = CLLocationCoordinate2D(
latitude: 53.347673,
longitude: -6.290198
)
let annaLiviaDescriptor = PlaceDescriptor(
representations: [.coordinate(annaLiviaCoordinates)],
commonName: "Anna Livia Fountain"
)
let request = MKMapItemRequest(placeDescriptor: annaLiviaDescriptor)
do {
annaLiviaMapItem = try await request.mapItem
} catch {
print("Error resolving placeDescriptor: \(error)")
} Creating and resolving a PlaceDescriptor with address PlaceRepresentation
// Creating and resolving a PlaceDescriptor with address PlaceRepresentation
import GeoToolbox
import MapKit
let address = "121-122 James's St, Dublin 8"
let descriptor = PlaceDescriptor(
representations: [.address(address)],
commonName: "Obelisk Fountain"
)
let request = MKMapItemRequest(placeDescriptor: descriptor)
do {
obeliskFountain = try await request.mapItem
} catch {
print("Error resolving placeDescriptor: \(error)")
} Creating a PlaceDescriptor with identifiers
// Creating a PlaceDescriptor with identifiers
import GeoToolbox
let annaLiviaCoordinates = CLLocationCoordinate2D(
latitude: 53.347673,
longitude: -6.290198
)
let identifiers = ["com.apple.MapKit" : "ICBB5FD7684CE949"]
let annaLiviaDescriptor = PlaceDescriptor(
representations: [.coordinate(annaLiviaCoordinates)],
commonName: "Anna Livia Fountain",
supportingRepresentations: [.serviceIdentifiers(identifiers)]
) Fetching a MapItem from a PlaceDescriptor
// Fetching a MapItem from a PlaceDescriptor
let request = MKMapItemRequest(placeDescriptor: descriptor)
let mapitem = try await request.mapItem Getting a PlaceDescriptor from a MapItem
// Getting a PlaceDescriptor from a MapItem
let descriptor = PlaceDescriptor(mapItem: mapitem) Place Card
// Place Card
var body: some View {
Map {
ForEach(fountains, id:\.name) { fountain in
Marker(item: fountain)
.mapItemDetailSelectionAccessory(.callout)
}
}
} Reverse geocode with MapKit
// Reverse geocode with MapKit
import MapKit
let millCreekCoordinates = CLLocation(latitude: 39.042617, longitude: -94.587526)
if let request = MKReverseGeocodingRequest(location: millCreekCoordinates) {
do {
let mapItems = try await request.mapItems
millCreekMapItem = mapItems.first
} catch {
print("Error reverse geocoding location: \(error)")
}
} Forward geocoding with MapKit
// Forward geocoding with MapKit
var body: some View {
Map {
if let mapItem {
Marker(item: mapItem)
}
}
.task {
let request = MKGeocodingRequest(
addressString: "1 Ferry Building, San Francisco"
)
do {
mapItem = try await request?.mapItems.first
} catch {
print("Error geocoding location: \(error)")
}
}
} Allowing Map Selection
// Allowing Map Selection
var selectedItem: MKMapItem?
var body: some View {
Map(selection: $selectedItem) {
UserAnnotation()
ForEach(fountains, id: \.self) { item in
Marker(item: item)
}
}
.onChange(of: selectedItem) {
// Compute Route
}
} Fetch a route
// Fetch a route
let request = MKDirections.Request()
request.source = MKMapItem.forCurrentLocation()
request.destination = selectedItem
let directions = MKDirections(request: request)
do {
let response = try await directions.calculate()
returnedRoutes = response.routes
} catch {
print("Error calculating directions: \(error)")
} Fetch a cycling route
// Fetch a cycling route
let request = MKDirections.Request()
request.source = MKMapItem.forCurrentLocation()
request.destination = selectedItem
request.transportType = .cycling
let directions = MKDirections(request: request)
do {
let response = try await directions.calculate()
returnedRoutes = response.routes
} catch {
print("Error calculating directions: \(error)")
} Display a route on the Map
// Display a route on the Map
Map {
if let mapRoute {
UserAnnotation()
MapPolyline(mapRoute)
.stroke(Color.blue, lineWidth: 5)
}
} Cycling directions in MapKit JS
// Cycling directions in MapKit JS
let directions = new mapkit.Directions();
directions.route ({
origin: safariPlayground,
destination: cherryHillFountain,
transportType: mapkit.Directions.Transport.Cycling
}, (error, { routes: [{ polyline }] }) => {
polyline.style.lineWidth = 5;
map.showItems([
new mapkit.PlaceAnnotation(place),
new mapkit.PlaceAnnotation(
place2,
{ selected: true }
),
polyline
]);
}); Look Around
// Look Around
var body: some View {
Map {
ForEach(fountains, id:\.name) { fountain in
Marker(item: fountain)
}
}
.overlay(alignment: .bottomLeading) {
if (lookAroundScene != nil) {
LookAroundPreview(scene: $lookAroundScene)
.frame(width: 230, height: 140)
.cornerRadius(10)
.padding(8)
}
}
} Look Around View in MapKit JS
// Look Around View in MapKit JS
const placeLookup = new mapkit.PlaceLookup();
const place = await new Promise(
resolve => placeLookup.getPlace(
"IBE1F65094A7A13B1",
(error, result) => resolve(result)
)
);
// Create an interactive look around view.
const lookAround = new mapkit.LookAround(
document.getElementById("container"),
place,
options
); Look Around Options
// Look Around Options for MapKit JS
const options = {
// Enters a full window experience
// immediately on load
openDialog: true,
// Provides a button to enter and
// exit full window.
showsDialogControl: true,
// Provides a button to destroy
// the look around view.
showsCloseControl: true,
}; Handle MapKit JS Look Around events
// Handle MapKit JS Look Around events
lookAround.addEventListener(
"close",
event => {
app.closeView();
event.preventDefault();
}
);
lookAround.addEventListener(
"load",
event => app.fadeInView()
);
lookAround.addEventListener(
"error",
event => app.fadeOutView()
);
lookAround.addEventListener(
"readystatechange",
event => console.log(lookAround.readyState)
); MapKit JS Look Around Preview
// MapKit JS Look Around Preview
const lookAround = new mapkit.LookAroundPreview(
document.getElementById("container"),
place
); Resources
Related sessions
-
17 min -
27 min -
42 min