Clustering with MapKit on iOS 11

Worth Baker
4 min readFeb 8, 2018

--

Coming out of WWDC 2017, one feature that flew pretty far under the radar was native annotation clustering in MapKit. Before iOS 11, getting clustering up and running in your app was somewhat laborious: you had to pick among several third-party libraries that offer the feature, add yet another dependency to your app, and put in a lot of work to make your existing architecture compatible with their system. All just to make your map a bit easier to read!

Fortunately, Apple’s native solution is both extremely easy to add to your app, and a natural fit with typical patterns one will see in most MapKit implementations. Let’s check it out.

For those that haven’t worked much with maps on iOS before, getting an annotation on screen is quite simple. In the graphic below, you’ll see a simple class hierarchy: an MKAnnotation, an MKMapView, and an MKAnnotationView sitting between them.

MKAnnotation is a protocol with one required property: a coordinate (CLLCoordinate2D). Objects that conform to this protocol can be added to a map, represented by MKMapView in MapKit. The actual view that is added to the map is an MKAnnotationView (or a subclass thereof), which will be placed on the map at the annotation object’s coordinate. Pretty simple!

So once we add a bunch of these annotations to the map, how do we cluster them? Also pretty simple: you take advantage of the new clusteringIdentifier property on MKAnnotationView. Any number of on-map annotations that share the same clusteringIdentifier can be combined together into a cluster by the system whenever the map’s viewport changes. Let’s dive into some code to see what this looks like in practice.

Here we have a UIViewController with a mapView property. In viewDidLoad, we generate a bunch of random annotations and add them to the map. Our annotations are of type MapItem, which conforms to MKAnnotation. Easy peasy.

Next, we need to indicate to MapKit that these annotations can be clustered together. Apple’s example project, Tandm (link is to a ZIP download), outlines a fairly simple process wherein you register MKAnnotationView subclasses with the mapView (similar to registering cells with a UITableView), and use some MapKit constants as their reuse identifiers (MKMapViewDefaultAnnotationViewReuseIdentifier and MKMapViewDefaultClusterAnnotationViewReuseIdentifier). As long as your MKAnnotationView subclasses have their clusteringIdentifer overridden and set, the system will perform clustering operations automatically.

That process, while simple, hides some of the mechanics as to how MapKit organizes its clustering system. In this example, we’ll be a bit more verbose and procedural, while also showing the guts of the system a bit more clearly.

With that long preamble, our next stop is in an MKMapViewDelegate method: Let’s take this step by step:

  1. We first attempt to unwrap the annotation as an MapItem, our standard annotation type.
  2. We generate a vanilla MKAnnotationView, either by having our MKMapView dequeue one ready for reuse, or by manually creating one of our own.
  3. We set up a few properties on the annotationView, noting specifically its clusteringIdentifier, as well as an image from our bundle (setting an image is the easiest way to give annotations a custom look in MapKit).
  4. Finally, if the annotation is not a MapItem, we check to see if its a system-generated MKClusterAnnotation, which are created automatically as the system needs to serve as the representation of a cluster of annotations. If it is, we again dequeue or create an MKAnnotationView and set up a couple of its properties, including an image from our bundle.

If you’ve previously worked with custom annotation views in MapKit, this should all look pretty standard. The only piece that may look new is the addition of the aforementioned clusteringIdentifier: any number of annotation views that share the same String identifier can be clustered together by the system.

If we build and run, we can see our clustering in action:

Clustering in action.

Boom! 🎉 A few lines of extra code, and we’ve added a great new feature to our app. There are, of course, some improvements we could make:

  • How about listing the number of items contained in each cluster?
  • Maybe some high-level information about the data contained in each cluster?
  • And, woof, the code is currently a bit messy. How about we clean it up, and make everything a bit more Swift-y?

We’ll run through these improvements next week in part two!

In the mean time, check out the working project on GitHub 👍

--

--