This is the cleanest way I have found to setup CoreLocation and MapView. First, you have to create an UIViewRepresentable to use CoreLocation in SwiftUI.
Do not forget to enable in your Info.plist file this Privacy with a message:
Privacy - Location Always and When In Use Usage Description
Privacy - Location When In Use Usage Description
Privacy - Location Always Usage Description
import SwiftUI
import MapKit
// MARK: Struct that handle the map
struct MapView: UIViewRepresentable {
@Binding var locationManager: CLLocationManager
@Binding var showMapAlert: Bool
let map = MKMapView()
///Creating map view at startup
func makeUIView(context: Context) -> MKMapView {
locationManager.delegate = context.coordinator
return map
}
func updateUIView(_ view: MKMapView, context: Context) {
map.showsUserLocation = true
map.userTrackingMode = .follow
}
///Use class Coordinator method
func makeCoordinator() -> MapView.Coordinator {
return Coordinator(mapView: self)
}
//MARK: - Core Location manager delegate
class Coordinator: NSObject, CLLocationManagerDelegate {
var mapView: MapView
init(mapView: MapView) {
self.mapView = mapView
}
///Switch between user location status
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
switch status {
case .restricted:
break
case .denied:
mapView.showMapAlert.toggle()
return
case .notDetermined:
mapView.locationManager.requestWhenInUseAuthorization()
return
case .authorizedWhenInUse:
return
case .authorizedAlways:
mapView.locationManager.allowsBackgroundLocationUpdates = true
mapView.locationManager.pausesLocationUpdatesAutomatically = false
return
@unknown default:
break
}
mapView.locationManager.startUpdatingLocation()
}
}
}
}
This is the way I use it in my SwiftUI view. An alert is toggle in case the permission is denied in the Coordinator class switch:
import SwiftUI
import CoreLocation
// MARK: View that shows map to users
struct HomeView: View {
@State var locationManager = CLLocationManager()
@State var showMapAlert = false
var body: some View {
MapView(locationManager: $locationManager, showMapAlert: $showMapAlert)
.alert(isPresented: $showMapAlert) {
Alert(title: Text("Location access denied"),
message: Text("Your location is needed"),
primaryButton: .cancel(),
secondaryButton: .default(Text("Settings"),
action: { self.goToDeviceSettings() }))
}
}
}
extension HomeView {
///Path to device settings if location is disabled
func goToDeviceSettings() {
guard let url = URL.init(string: UIApplication.openSettingsURLString) else { return }
UIApplication.shared.open(url, options: [:], completionHandler: nil)
}
}
Core Location
- also make sure that the current scheme allows location simulation:Current Scheme -> Edit -> Run -> Options ->Allow Location Simulation
– Congou