Displaying current location on Map using CoreLocation and MapKit in SwiftUI
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
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.