Displaying current location on Map using CoreLocation and MapKit in SwiftUI

Meet Patel
5 min readNov 15, 2023

--

Full Code is uploaded on my Github

Apple’s MapKit framework, designed for embedding maps into iOS apps, has effortlessly integrated into SwiftUI, Apple’s declarative UI framework. This blog post will take you through the process of incorporating MapKit into your SwiftUI apps, allowing you to create MapView and display current location of User.

Importing MapKit and Setting a Map Region

Before diving into the map display, we’ll start by importing the MapKit framework and establishing a map region. This map region defines the initial area that will be shown on the map.

import SwiftUI
import MapKit

mapRegion variable using MapKit in Swift. It defines a geographic region with a center at latitude 37.785834 and longitude -122.406417. The visible span of the map is set to 0.2 in both latitude and longitude, creating a rectangular area focused on a specific location, often used as an initial view for a map.

@State var mapRegion = MKCoordinateRegion(center: CLLocationCoordinate2D(latitude: 37.785834, longitude: -122.406417), span: MKCoordinateSpan(latitudeDelta: 0.2, longitudeDelta: 0.2))

Bringing the Map to Life with User Location Tracking

To display the map within your SwiftUI view, simply add a Map element and pass the defined map region as a parameter. To enable user location tracking, simply set the showsUserLocation property to true.

Map(coordinateRegion: $mapRegion, showsUserLocation: true)

Enhancing the Map Experience with User Tracking Modes

MapKit provides a variety of user tracking modes to improve the map experience. The userTrackingMode property allows you to specify the desired tracking mode, such as .follow to keep the user's location centered on the map.

Map(coordinateRegion: $mapRegion, showsUserLocation: true, userTrackingMode: .constant(.follow))
import SwiftUI
import MapKit

struct ContentView: View {

@State var mapRegion = MKCoordinateRegion(center: CLLocationCoordinate2D(latitude: 37.785834, longitude: -122.406417), span: MKCoordinateSpan(latitudeDelta: 0.2, longitudeDelta: 0.2))

var body: some View {
VStack {
Map(coordinateRegion: viewModel.binding,showsUserLocation: true,userTrackingMode: .constant(.follow))
.edgesIgnoringSafeArea(.all)

}

}
}

Managing Location Authorization and Updates

How It Works

This app begins by creating an instance of the ContentViewModel, which handles the logic for managing location services. The ContentView uses the Map view from SwiftUI, and its coordinateRegion is bound to the mapRegion property of the ViewModel. This enables the map to dynamically update based on the user's location.

Create ContentViewModel class with CLLocationManagerDelegate

final class ContentViewModel: NSObject, ObservableObject, CLLocationManagerDelegate {
var locationManager: CLLocationManager?
}

Location Authorization

We have to allow the privacy property. So navigate to your root directory then target->info and then click on any property and press enter. It will create a new one for you and now write as shown in picture

Now inside the ContentViewModel checks if location services are enabled and requests the necessary authorization. It responds to changes in authorization status and updates the map accordingly.

final class ContentViewModel: NSObject, ObservableObject, CLLocationManagerDelegate {
var locationManager: CLLocationManager?

func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {
// Handle changes in location authorization
let previousAuthorizationStatus = manager.authorizationStatus
manager.requestWhenInUseAuthorization()
if manager.authorizationStatus != previousAuthorizationStatus {
checkLocationAuthorization()
}
}
}

Setting Up Location Services

The checkIfLocationIsEnabled inside ContentViewModel is a crucial step in setting up location services within your app. It first checks if location services are enabled on the device using CLLocationManager.locationServicesEnabled(). If enabled, it initializes a CLLocationManager instance, a key component for handling location-related tasks. The desired accuracy is set to kCLLocationAccuracyBest to ensure high precision.

func checkIfLocationIsEnabled() {
if CLLocationManager.locationServicesEnabled() {
locationManager = CLLocationManager()
locationManager?.desiredAccuracy = kCLLocationAccuracyBest
locationManager!.delegate = self
} else {
print("Show an alert letting them know this is off")
}
}

By designating the current object as the delegate (locationManager!.delegate = self), the app can respond to location-related events. If location services are not enabled, it prints a message to the console. In a complete app, this message would typically trigger an alert to inform the user that enabling location services is necessary for optimal app functionality.

Example

Updating Map Region

When location authorization is granted, the ViewModel updates the mapRegion property, centring the map on the user's location.

private func checkLocationAuthorization() {
// Check location authorization status
guard let location = locationManager else {
return
}
switch location.authorizationStatus {
case .notDetermined:
print("Location authorization is not determined.")
case .restricted:
print("Location is restricted.")
case .denied:
print("Location permission denied.")
case .authorizedAlways, .authorizedWhenInUse:
// Update map region with user's location
if let location = location.location {
mapRegion = MKCoordinateRegion(
center: location.coordinate,
span: MKCoordinateSpan(latitudeDelta: 0.2, longitudeDelta: 0.2)
)
}
default:
break
}

Here’s the full code

You all are wondering what is @ Published doing here. So main task of publish is to update the app when current location of user changes. For example when a person drives a car so location of use is updated automatically.

//
// ContentView.swift
// MapKitApp
//
// Created by Meet Patel on 2023-11-14.
//

import SwiftUI
import MapKit

struct ContentView: View {

@StateObject var viewModel = ContentViewModel()

var body: some View {
VStack {
Map(coordinateRegion: viewModel.binding,showsUserLocation: true,userTrackingMode: .constant(.follow))
.edgesIgnoringSafeArea(.all)
.onAppear(perform: {
viewModel.checkIfLocationIsEnabled()
})

}

}
}


final class ContentViewModel: NSObject, ObservableObject, CLLocationManagerDelegate {
var locationManager: CLLocationManager?

@Published var mapRegion = MKCoordinateRegion(center: CLLocationCoordinate2D(latitude: 43.457105, longitude: -80.508361), span: MKCoordinateSpan(latitudeDelta: 0.2, longitudeDelta: 0.2))

var binding: Binding<MKCoordinateRegion> {
Binding {
self.mapRegion
} set: { newRegion in
self.mapRegion = newRegion
}
}

func checkIfLocationIsEnabled() {
if CLLocationManager.locationServicesEnabled() {
locationManager = CLLocationManager()
locationManager?.desiredAccuracy = kCLLocationAccuracyBest
locationManager!.delegate = self
} else {
print("Show an alert letting them know this is off")
}
}

func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {
let previousAuthorizationStatus = manager.authorizationStatus
manager.requestWhenInUseAuthorization()
if manager.authorizationStatus != previousAuthorizationStatus {
checkLocationAuthorization()
}
}

private func checkLocationAuthorization() {
guard let location = locationManager else {
return
}

switch location.authorizationStatus {
case .notDetermined:
print("Location authorization is not determined.")
case .restricted:
print("Location is restricted.")
case .denied:
print("Location permission denied.")
case .authorizedAlways, .authorizedWhenInUse:
if let location = location.location {
mapRegion = MKCoordinateRegion(center: location.coordinate, span: MKCoordinateSpan(latitudeDelta: 0.2, longitudeDelta: 0.2))
}

default:
break
}
}
}


struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}

Demo Example

Demo

Conclusion

By following these steps, you’ve acquired the essential knowledge to integrate MapKit into your SwiftUI applications.

MapKit in SwiftUI offers a powerful and versatile toolset for crafting visually appealing and interactive map experiences. By leveraging SwiftUI’s declarative nature, you can seamlessly incorporate maps into your applications while maintaining clean and maintainable code.

--

--