Codakuma
iOS apps and more

Adding polylines to maps in SwiftUI

03 Mar 2021

As of March 2021, SwiftUI has a built-in map view but it’s quite simple, only supporting basic annotations.

Recently I wrote about making use of this map view for my app Personal Best, where I settled on using dots to represent GPS pings as polylines weren’t available.

At the time this felt like a reasonable compromise, but since then I’ve gotten a lot better at SwiftUI and interfacing with UIKit in particular, so I’ve been exploring how to bridge a map view featuring a polyline to SwiftUI.

This simple tutorial will teach you how to make a map view component in SwiftUI that includes a polyline.

It’s based on hackingswithswift.com’s ‘Advanced MKMapView with SwiftUI’ tutorial, which I combined with a StackOverflow answer from Daniel E. Salinas.

View the code for this tutorial on GitHub

Building the basic map view

Add the following code, which will make a basic map that doesn’t include the polyline yet.

MapView.swift

import SwiftUI
import MapKit

struct MapView: UIViewRepresentable {

  let region: MKCoordinateRegion
  let lineCoordinates: [CLLocation]

  // Create the MKMapView using UIKit.
  func makeUIView(context: Context) -> MKMapView {
    let mapView = MKMapView()
    mapView.delegate = context.coordinator
    mapView.region = region
    return mapView
  }

  // We don't need to worry about this as the view will never be updated.
  func updateUIView(_ view: MKMapView, context: Context) {}

  // Link it to the coordinator which is defined below.
  func makeCoordinator() -> Coordinator {
    Coordinator(self)
  }

}

class Coordinator: NSObject, MKMapViewDelegate {
  var parent: MapWithPolyline

  init(_ parent: MapWithPolyline) {
    self.parent = parent
  }
}

ContentView.swift

import SwiftUI

struct ContentView: View {
  @State private var region = MKCoordinateRegion(
    // Apple Park
    center: CLLocationCoordinate2D(latitude: 37.334803, longitude: -122.008965),
    span: MKCoordinateSpan(latitudeDelta: 0.01, longitudeDelta: 0.01)
  )

  var body: some View {
    MapView(region: region)
      .edgesIgnoringSafeArea(.all)
  }
}

At this point, running our code locally just shows a basic map, centered on the region we specified in ContentView.swift.

Basic MapView

Adding a polyline

Add a new lineCoordinates property to MapView. It’ll accept an array of location coordinates, which will be used to draw the line.

let lineCoordinates: [CLLocationCoordinate2D]

Now add some code to makeUIView to draw the polyline based on the coordinates:

let polyline = MKPolyline(coordinates: lineCoordinates, count: lineCoordinates.count)
mapView.addOverlay(polyline)

Finally, add a new function to Coordinator which will define how the polyline is rendered.

func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
  if let routePolyline = overlay as? MKPolyline {
    let renderer = MKPolylineRenderer(polyline: routePolyline)
    renderer.strokeColor = UIColor.systemBlue
    renderer.lineWidth = 5
    return renderer
  }
  return MKOverlayRenderer()
}

Now, update ContentView to provide lineCoordinates:

struct ContentView: View {
  @State private var region = MKCoordinateRegion(
    // Apple Park
    center: CLLocationCoordinate2D(latitude: 37.334803, longitude: -122.008965),
    span: MKCoordinateSpan(latitudeDelta: 0.01, longitudeDelta: 0.01)
  )

  @State private var lineCoordinates = [
    // Steve Jobs theatre
    CLLocationCoordinate2D(latitude: 37.330828, longitude: -122.007495),
    // Caffè Macs
    CLLocationCoordinate2D(latitude: 37.336083, longitude: -122.007356),
    // Apple wellness center
    CLLocationCoordinate2D(latitude: 37.336901, longitude:  -122.012345)
  ];

  var body: some View {
    MapView(region: region, lineCoordinates: lineCoordinates)
      .edgesIgnoringSafeArea(.all)
  }
}

Run the code and now the line is visible.

MapView with polyline

Conclusion and next steps

Today we learned how to bridge MKMapView from UIKit to SwiftUI and add a polyline to it. This is the most basic implementation possible, but there are lots of ways this could be improved, for example adding customisation of the polyline, making it optional, adding the ability to add other types of annotation, and so on.

If you have any questions or comments, feel free to reach out via Twitter or email. Links to both are in the footer of this page.

View the code for this tutorial on GitHub