🗺️ MapKit in 2 steps — Basic implementation for UIKit

Tales Silveira
Poatek
Published in
4 min readSep 29, 2023

--

This is the type of article that I really enjoy writing, because it's not a hard feature to do but at the same time you usually need to find 3 to 4 different sources before you can actually make it work.

So, hopefully this article will be the only one you'll need.

Step 1: The view controller setup.

  • Import MapKit
  • Instantiate MKMapView and CLLocationManager objects
  • Implement and setup the delegate methods
import UIKit
import MapKit

// MARK: - IB Outlets
@IBOutlet var map: MKMapView!

// MARK: - Stored Properties
let locationManager = CLLocationManager()

// MARK: - CLLocationManagerDelegate
extension MainViewController: CLLocationManagerDelegate {

func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
centerViewOnUserLocation()
}

func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {
checkLocationAuthorization()
}
}

// MARK: - Life Cycle
extension MainViewController {
override func viewDidLoad() {
super.viewDidLoad()
locationManager.delegate = self
map.delegate = self
}
}

These delegate methods are self-explanatory, but just to make sure:

  • Location manager: it's going to be called when the location is updated, very useful for cases where you want to center the map around your user's location (updating a car position along the way, for instance). Here's just one example of code you can use, but remember it should be the one that best suits for your needs.
//a minor value means more zoom
let scale: CGFloat = 300

private func centerViewOnUserLocation() {
if let coordinate = locationManager.location?.coordinate {
let region = MKCoordinateRegion.init(center: coordinate,
latitudinalMeters: scale,
longitudinalMeters: scale)
map.setRegion(region, animated: true)
}
}
  • LocationManagerDidChangeAuthorization: This method is mandatory to everyone that opened the app for the first time and needs to authorize/enable location on their iPhone. In here, we're gonna call a simple method to manage this authorization:
private func checkLocationAuthorization() {
switch locationManager.authorizationStatus {
case .authorizedWhenInUse, .authorizedAlways:
map.showsUserLocation = true
locationManager.startUpdatingLocation()
default:
locationManager.requestWhenInUseAuthorization()
}
}

Step 2: Info.plist configuration

When using device authorization in general, we usually need to change the Info.plist. It's not different for this case.

Adding to that, I'd say by experience that Apple would like a custom sentence according to the project when asking for authorization, something like:
"Allows [appName] permission to use your location to [find something nearby/update locations/create pins/whatever]"

That's it! Yeah, it's that easy.

This is basically what you need to start using MapKit for UIKit.

BUT
Of course I wouldn't leave without adding some good tips and methods to help you to customize your experience. Stick here if that's your case!

Useful methods:

  • Current location distance: Get the distance between the user current location and another coordinate:
private func getLocationDistance(location: MKMapItem) -> Double? {
guard let userLocation else { return nil }
let location = location.placemark.location?.distance(from: userLocation)
return location
}
  • Add custom annotation (pin): To add custom annotations/pins in the map you should create a list of MKMapItem. After that, you can use a method like this to populate them:
let coordinate = CLLocationCoordinate2DMake(latitude, longitude)
let item = MKMapItem(placemark: MKPlacemark(coordinate: coordinate)
item.name = "Mc Donalds"
item.phoneNumber = "51 3333.2222"
let mapItems = [item, ...]

private func addMapAnnotations() {
for item in mapItems {
let annotation = MKPointAnnotation()
annotation.coordinate = item.placemark.coordinate
//you can add whatever info you want to your pin
annotation.title = item.name
annotation.subtitle = item.phoneNumber
//you could only add (show) locations of a specific range
let range = 1000
if let distance = getLocationDistance(location: item), distance < range {
self.map.addAnnotation(annotation)
}
}
}
  • Search by queries: This method will find locations on a specific region that matches the query:
func showPointsOfCategory(_ query: String) {
let searchRequest = MKLocalSearch.Request()
searchRequest.naturalLanguageQuery = query // i.e. "Restaurant", "Hospital", "Poatek"
searchRequest.region = map.region

let search = MKLocalSearch(request: searchRequest)
search.start { [weak self] response, error in
guard let self = self,
let response = response else { return }

self.mapItems.append(contentsOf: response.mapItems)
self.addMapAnnotations()
self.map.setRegion(response.boundingRegion, animated: true)
}
}
  • Zoom in a specific annotation:
private func goToChosenLocation(location: MKMapItem) {
let coordinateRegion = MKCoordinateRegion(center: location.placemark.coordinate,
latitudinalMeters: scale,
longitudinalMeters: scale)
map.setRegion(coordinateRegion, animated: true)
}

Useful informations

  • Placemark parameters: You can access additional information related to the locations using MKMapItem.placemark.option. Here are some options you can use:
name = "Don Julio";
phoneNumber = "+54 11 4831 9564";
placemark = "Don Julio, Guatemala 4691, C1425 Buenos Aires, Ciudad Aut\U00f3noma de Buenos Aires, Argentina @ <-34.58634000,-58.42423000> +/- 0.00m, region CLCircularRegion (identifier:'<-34.58634000,-58.42423000> radius 141.17', center:<-34.58634000,-58.42423000>, radius:141.17m)";
timeZone = "America/Argentina/Buenos_Aires (GMT-3) offset -10800";
url = "https://www.parrilladonjulio.com";
  • Simulator location: You can set your custom location tapping the arrow icon in the bottom menu, like this:

I hope I helped you on your iOS dev journey!

See you next time :)

--

--