User Location

Screenshot

This example demonstrates how to show uer location on the map and request precise location temporarily. You will learn the following:

  • How to overlay button on top of the MapView and how to coordinate view state between them.
  • How to setup the app so that it has access to location.
  • How to configure the MapView so that the user location is shown on the map.
  • How to temporarily request precise location if user opted out in approximate location.

More information about iOS location management: https://developer.apple.com/documentation/corelocation/getting_the_user_s_location/using_the_standard_location_service

More information about iOS location manager: https://developer.apple.com/documentation/corelocation/cllocationmanager

Mobile App - Setup the MapView

In order to enable the alert that requests temporary precise location, please add the following into info.plist:

<key>NSLocationTemporaryUsageDescriptionDictionary</key>
<dict>
    <key>MGLAccuracyAuthorizationDescription</key>
    <string>Examples requires your precise location to show your location.</string>
</dict>

Request location

  1. Next, you need to request user location in the MapView

     mapView.showsUserLocation = true
    

Request Precise Location Temporarily

  1. Our example uses SwiftUI. We conditionally add a button for requesting precise location on top of the map view when the current accuracy settings is not precise. So we have two views:

    • LocationAwareMapView which is wrapping MGLMapView and makes it possible to use it in SwiftUI. In this view we will use MapView methods to request precise location and the MGLMapViewDelegate to respond to event which is raised when location accuracy has been changed.
    • Second view will contain button used to request precise location when tapped.

    We need to coordinate UI state - when the precise location is requested and user accepts it, the MGLMapViewDelegate will raise mapViewDidFinishLoadingMap event and we hide the button. To coordinate the state we use class derived from ObservableObject which contain just a flag indicating if the button should be visible or not.

          class SharedState: ObservableObject {
     init(showPreciseButton: Bool) {
         self.showPreciseButton = showPreciseButton
     }
     @Published var showPreciseButton: Bool
    }
    
  2. The method which handles MGLMapViewDelegate event raised when accuracy settings changed looks as follows:

         func mapViewDidFinishLoadingMap(_ mapView: MGLMapView,
                                     didChangeLocationManagerAuthorization manager: MGLLocationManager) {
         guard let accuracySetting = manager.accuracyAuthorization?() else { return }
         if accuracySetting == .reducedAccuracy {
             sharedState.showPreciseButton = true;
         } else {
             sharedState.showPreciseButton = false;
         }
     }
    
  3. And the method for requesting precise location :

         func requestTemporaryAuth() {
         guard let mapView = self.mapView else { return }
             
         let purposeKey = "MGLAccuracyAuthorizationDescription"
         mapView.locationManager.requestTemporaryFullAccuracyAuthorization!(withPurposeKey: purposeKey)
     }
    
  4. The full code of the UI:

          class LocationAwareMapView: UIViewRepresentable {
     @ObservedObject var sharedState: SharedState
     var mapView: MGLMapView?
        
     init(_ sharedState: SharedState) {
        self.sharedState = sharedState
     }
    
        
     func makeUIView(context: Context) -> MGLMapView {
         // read the key from property list
         let mapTilerKey = Helper.getMapTilerkey()
         Helper.validateKey(mapTilerKey)
            
         // Build the style url
         let styleURL = URL(string: "https://api.maptiler.com/maps/streets-v2/style.json?key=\(mapTilerKey)")
            
         // create the mapview
         let mapView = MGLMapView(frame: .zero, styleURL: styleURL)
         mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
         mapView.logoView.isHidden = true
         mapView.showsUserLocation = true
            
         // set the map view delegate so that we can respond to events
         mapView.delegate = context.coordinator
            
         // keep map view reference, it will be needed when requesting permissions
         self.mapView = mapView;
            
         return mapView
     }
        
     func updateUIView(_ uiView: MGLMapView, context: Context) {}
        
     func makeCoordinator() -> LocationAwareMapView.Coordinator {
         Coordinator(self, sharedState)
     }
        
        
     final class Coordinator: NSObject, MGLMapViewDelegate {
         var control: LocationAwareMapView
            
         @ObservedObject var sharedState: SharedState
            
         init(_ control: LocationAwareMapView, _ sharedState: SharedState) {
             self.control = control
             self.sharedState = sharedState
         }
     }
        
     @available(iOS 14, *)
        
     func mapViewDidFinishLoadingMap(_ mapView: MGLMapView,
                                     didChangeLocationManagerAuthorization manager: MGLLocationManager) {
         guard let accuracySetting = manager.accuracyAuthorization?() else { return }
         if accuracySetting == .reducedAccuracy {
             sharedState.showPreciseButton = true;
         } else {
             sharedState.showPreciseButton = false;
         }
     }
        
     @available(iOS 14, *)
        
     @objc public func requestTemporaryAuth() {
         guard let mapView = self.mapView else { return }
             
         let purposeKey = "MGLAccuracyAuthorizationDescription"
         mapView.locationManager.requestTemporaryFullAccuracyAuthorization!(withPurposeKey: purposeKey)
     }
    }
    
  5. The full code of the wrapped MapView:

           struct LocationPermissions: View {
     @ObservedObject var sharedState: SharedState;
     var wrappedMapView: LocationAwareMapView;
     init() {
         let sharedState = SharedState(showPreciseButton: true)
         self.wrappedMapView = LocationAwareMapView(sharedState)
         self.sharedState = sharedState
     }
    
     var body: some View {
         ZStack {
             wrappedMapView.navigationTitle("Location")
             if sharedState.showPreciseButton {
                 VStack {
                     Button(
                         action: {wrappedMapView.requestTemporaryAuth()},
                         label: {Text("Turn Precise On")})
                         .background(Color.gray)
                 }
             }
         }
     }
    }
    
iOS SDK

SDK JS Reference

On this page