Marker Clustering
- SwiftUI
This example demonstrates how to render thousands of campsites loaded from a bundled GeoJSON and cluster them natively with Maps SDK for iOS. At low zoom the markers group into count "pill" bubbles; as you zoom in the clusters break apart into per-type coloured pins (green = info-only, red = bookable). Tapping a cluster fits the map to its members to split it apart; tapping a pin centres the map on it and shows its details in an info panel — at the bottom in portrait, bottom-left in landscape.
Check the full implementation on GitHub.

How it works
The map uses two overlapping marker collections. A marker that carries its own per-marker render settings suppresses the SDK's cluster count label, so the count and the coloured pins cannot come from a single collection:
- Cluster layer — every campsite, native grouping → density "pill" bubbles with the count label. It has no
customMarkerSettings, and its loose (ungrouped) markers draw a transparent image. - Detail layer — the same full set, grouped at the same zoom but with transparent group images (so it shows nothing while clustered). Each loose pin is coloured green/red through the
customMarkerSettingsclosure.
Because both layers hold the same marker set and group at the same zoom, they make identical group/loose decisions, so a pill never sits on top of a loose pin and the count always comes from the cluster layer.
Both layers set buildPointsGroupConfig = true. A tap hits the topmost (detail) layer, so it needs the group config in order to enumerate a cluster's members (via getPointsGroupHead / getPointsGroupComponents) when the cluster is tapped.
The two-collection renderer


Loading the map and handling taps
The map opens on a continental-US view; a "Load campsites" button parses the bundled GeoJSON off the main thread and renders the layers. On tap, a .coordinateGroup match is a cluster: its members are fitted to their bounding box with center(onArea:) to split it apart. A single pin centres the map on it (center(onCoordinates:zoomLevel:-1:) keeps the current zoom) and shows its info panel.