Dunfey · Hotel WWDC as data, est. 1983
Front desk everything
Years
Topics

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 ↗

Transcript all transcripts

Chapters

Code shown on screen · 19 snippets

Putting Marker on the Map with a coordinate swift · at 4:49 ↗
// 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 swift · at 5:07 ↗
// 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 swift · at 5:56 ↗
// 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 swift · at 6:45 ↗
// 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 swift · at 7:28 ↗
// Fetching a MapItem from a PlaceDescriptor

let request = MKMapItemRequest(placeDescriptor: descriptor)
let mapitem = try await request.mapItem
Getting a PlaceDescriptor from a MapItem swift · at 7:43 ↗
// Getting a PlaceDescriptor from a MapItem

let descriptor = PlaceDescriptor(mapItem: mapitem)
Place Card swift · at 8:10 ↗
// Place Card

var body: some View {
    Map {
        ForEach(fountains, id:\.name) { fountain in
            Marker(item: fountain)
                .mapItemDetailSelectionAccessory(.callout)
        }
    }
}
Reverse geocode with MapKit swift · at 10:45 ↗
// 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 swift · at 13:50 ↗
// 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 swift · at 14:38 ↗
// Allowing Map Selection

@State 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 swift · at 15:00 ↗
// 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 swift · at 16:06 ↗
// 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 swift · at 16:25 ↗
// Display a route on the Map

Map {
    if let mapRoute {
        UserAnnotation()
        MapPolyline(mapRoute)
            .stroke(Color.blue, lineWidth: 5)
    }
}
Cycling directions in MapKit JS javascript · at 16:40 ↗
// 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 swift · at 17:26 ↗
// 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 javascript · at 18:10 ↗
// 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 javascript · at 18:35 ↗
// 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 javascript · at 19:10 ↗
// 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 javascript · at 20:01 ↗
// MapKit JS Look Around Preview

const lookAround = new mapkit.LookAroundPreview(
    document.getElementById("container"),
    place
);

Resources