Improvements & Fixes: User Location and Location Privacy Example iOS (#2393)
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
This commit is contained in:
@@ -6,14 +6,16 @@ Requesting precise location with ``MLNLocationManager``.
|
||||
|
||||
This example shows how to request a precise location with ``MLNLocationManager``.
|
||||
|
||||
First of all you need to prepare your `Info.plist`. You need to provide a description why your app needs to access location:
|
||||
Let's prepare your `Info.plist`:
|
||||
|
||||
First, provide a description why your app needs to access location:
|
||||
|
||||
```plist
|
||||
<key>NSLocationWhenInUseUsageDescription</key>
|
||||
<string>Dummy Location When In Use Description</string>
|
||||
```
|
||||
|
||||
As well as a description why your app needs precise location.
|
||||
Second, add a description why your app needs precise location:
|
||||
|
||||
```plist
|
||||
<key>NSLocationTemporaryUsageDescriptionDictionary</key>
|
||||
@@ -23,6 +25,13 @@ As well as a description why your app needs precise location.
|
||||
</dict>
|
||||
```
|
||||
|
||||
Third and finally, specify your app only needs reduced location access by default (until you request precise accuracy in the example):
|
||||
|
||||
```plist
|
||||
<key>NSLocationDefaultAccuracyReduced</key>
|
||||
<true/>
|
||||
```
|
||||
|
||||
The `Coordinator` defined below is also the ``MLNMapViewDelegate``. When the location manager authorization changes it will call the relevant method. If precise location has not been granted, a button is shown at the bottom of the map.
|
||||
|
||||

|
||||
@@ -40,75 +49,85 @@ enum LocationAccuracyState {
|
||||
case fullAccuracy
|
||||
}
|
||||
|
||||
class Coordinator: NSObject, MLNMapViewDelegate {
|
||||
@Binding var mapView: MLNMapView
|
||||
@Binding var locationAccuracy: LocationAccuracyState
|
||||
var pannedToUserLocation = false
|
||||
@MainActor
|
||||
class PrivacyExampleViewModel: NSObject, ObservableObject {
|
||||
@Published var locationAccuracy: LocationAccuracyState = .unknown
|
||||
@Published var showTemporaryLocationAuthorization = false
|
||||
}
|
||||
|
||||
init(mapView: Binding<MLNMapView>, locationAccuracy: Binding<LocationAccuracyState>) {
|
||||
_mapView = mapView
|
||||
_locationAccuracy = locationAccuracy
|
||||
class PrivacyExampleCoordinator: NSObject, MLNMapViewDelegate {
|
||||
@ObservedObject private var mapViewModel: PrivacyExampleViewModel
|
||||
private var pannedToUserLocation = false
|
||||
|
||||
init(mapViewModel: PrivacyExampleViewModel) {
|
||||
self.mapViewModel = mapViewModel
|
||||
super.init()
|
||||
}
|
||||
|
||||
func mapView(_: MLNMapView, didChangeLocationManagerAuthorization manager: MLNLocationManager) {
|
||||
@MainActor func mapView(_: MLNMapView, didChangeLocationManagerAuthorization manager: MLNLocationManager) {
|
||||
guard let accuracySetting = manager.accuracyAuthorization else {
|
||||
return
|
||||
}
|
||||
|
||||
switch accuracySetting() {
|
||||
case .fullAccuracy:
|
||||
locationAccuracy = .fullAccuracy
|
||||
mapViewModel.locationAccuracy = .fullAccuracy
|
||||
case .reducedAccuracy:
|
||||
locationAccuracy = .reducedAccuracy
|
||||
mapViewModel.locationAccuracy = .reducedAccuracy
|
||||
@unknown default:
|
||||
locationAccuracy = .unknown
|
||||
mapViewModel.locationAccuracy = .unknown
|
||||
}
|
||||
}
|
||||
|
||||
// when a location is available for the first time, we fly to it
|
||||
func mapView(_ mapView: MLNMapView, didUpdate _: MLNUserLocation?) {
|
||||
if pannedToUserLocation {
|
||||
return
|
||||
}
|
||||
guard !pannedToUserLocation else { return }
|
||||
guard let userLocation = mapView.userLocation else {
|
||||
print("User location is currently not available.")
|
||||
return
|
||||
}
|
||||
mapView.fly(to: MLNMapCamera(lookingAtCenter: userLocation.coordinate, altitude: 100_000, pitch: 0, heading: 0))
|
||||
pannedToUserLocation = true
|
||||
}
|
||||
}
|
||||
|
||||
struct LocationPrivacyExample: UIViewRepresentable {
|
||||
@Binding var mapView: MLNMapView
|
||||
@Binding var locationAccuracy: LocationAccuracyState
|
||||
struct PrivacyExampleRepresentable: UIViewRepresentable {
|
||||
@ObservedObject var mapViewModel: PrivacyExampleViewModel
|
||||
|
||||
func makeCoordinator() -> Coordinator {
|
||||
Coordinator(mapView: $mapView, locationAccuracy: $locationAccuracy)
|
||||
func makeCoordinator() -> PrivacyExampleCoordinator {
|
||||
PrivacyExampleCoordinator(mapViewModel: mapViewModel)
|
||||
}
|
||||
|
||||
func makeUIView(context: Context) -> MLNMapView {
|
||||
let mapView = MLNMapView()
|
||||
mapView.showsUserLocation = true
|
||||
mapView.delegate = context.coordinator
|
||||
|
||||
mapView.delegate = context.coordinator
|
||||
mapView.showsUserLocation = true
|
||||
return mapView
|
||||
}
|
||||
|
||||
func updateUIView(_: MLNMapView, context _: Context) {}
|
||||
func updateUIView(_ mapView: MLNMapView, context _: Context) {
|
||||
if mapViewModel.showTemporaryLocationAuthorization {
|
||||
let purposeKey = "MLNAccuracyAuthorizationDescription"
|
||||
mapView.locationManager.requestTemporaryFullAccuracyAuthorization?(withPurposeKey: purposeKey)
|
||||
DispatchQueue.main.async {
|
||||
mapViewModel.showTemporaryLocationAuthorization = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct LocationPrivacyExampleView: View {
|
||||
@State private var mapView = MLNMapView()
|
||||
@State var locationAccuracy: LocationAccuracyState = .unknown
|
||||
@StateObject private var viewModel = PrivacyExampleViewModel()
|
||||
|
||||
var body: some View {
|
||||
VStack {
|
||||
LocationPrivacyExample(mapView: $mapView, locationAccuracy: $locationAccuracy)
|
||||
PrivacyExampleRepresentable(mapViewModel: viewModel)
|
||||
.edgesIgnoringSafeArea(.all)
|
||||
|
||||
if locationAccuracy == LocationAccuracyState.reducedAccuracy {
|
||||
if viewModel.locationAccuracy == LocationAccuracyState.reducedAccuracy {
|
||||
Button("Request Precise Location") {
|
||||
handleButtonPress(mapView: mapView)
|
||||
viewModel.showTemporaryLocationAuthorization.toggle()
|
||||
}
|
||||
.padding()
|
||||
.background(Color.blue)
|
||||
@@ -117,16 +136,5 @@ struct LocationPrivacyExampleView: View {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func handleButtonPress(mapView: MLNMapView) {
|
||||
print("Requesting precice location")
|
||||
switch locationAccuracy {
|
||||
case .reducedAccuracy:
|
||||
let purposeKey = "MLNAccuracyAuthorizationDescription"
|
||||
mapView.locationManager.requestTemporaryFullAccuracyAuthorization!(withPurposeKey: purposeKey)
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -9,75 +9,85 @@ enum LocationAccuracyState {
|
||||
case fullAccuracy
|
||||
}
|
||||
|
||||
class Coordinator: NSObject, MLNMapViewDelegate {
|
||||
@Binding var mapView: MLNMapView
|
||||
@Binding var locationAccuracy: LocationAccuracyState
|
||||
var pannedToUserLocation = false
|
||||
@MainActor
|
||||
class PrivacyExampleViewModel: NSObject, ObservableObject {
|
||||
@Published var locationAccuracy: LocationAccuracyState = .unknown
|
||||
@Published var showTemporaryLocationAuthorization = false
|
||||
}
|
||||
|
||||
init(mapView: Binding<MLNMapView>, locationAccuracy: Binding<LocationAccuracyState>) {
|
||||
_mapView = mapView
|
||||
_locationAccuracy = locationAccuracy
|
||||
class PrivacyExampleCoordinator: NSObject, MLNMapViewDelegate {
|
||||
@ObservedObject private var mapViewModel: PrivacyExampleViewModel
|
||||
private var pannedToUserLocation = false
|
||||
|
||||
init(mapViewModel: PrivacyExampleViewModel) {
|
||||
self.mapViewModel = mapViewModel
|
||||
super.init()
|
||||
}
|
||||
|
||||
func mapView(_: MLNMapView, didChangeLocationManagerAuthorization manager: MLNLocationManager) {
|
||||
@MainActor func mapView(_: MLNMapView, didChangeLocationManagerAuthorization manager: MLNLocationManager) {
|
||||
guard let accuracySetting = manager.accuracyAuthorization else {
|
||||
return
|
||||
}
|
||||
|
||||
switch accuracySetting() {
|
||||
case .fullAccuracy:
|
||||
locationAccuracy = .fullAccuracy
|
||||
mapViewModel.locationAccuracy = .fullAccuracy
|
||||
case .reducedAccuracy:
|
||||
locationAccuracy = .reducedAccuracy
|
||||
mapViewModel.locationAccuracy = .reducedAccuracy
|
||||
@unknown default:
|
||||
locationAccuracy = .unknown
|
||||
mapViewModel.locationAccuracy = .unknown
|
||||
}
|
||||
}
|
||||
|
||||
// when a location is available for the first time, we fly to it
|
||||
func mapView(_ mapView: MLNMapView, didUpdate _: MLNUserLocation?) {
|
||||
if pannedToUserLocation {
|
||||
return
|
||||
}
|
||||
guard !pannedToUserLocation else { return }
|
||||
guard let userLocation = mapView.userLocation else {
|
||||
print("User location is currently not available.")
|
||||
return
|
||||
}
|
||||
mapView.fly(to: MLNMapCamera(lookingAtCenter: userLocation.coordinate, altitude: 100_000, pitch: 0, heading: 0))
|
||||
pannedToUserLocation = true
|
||||
}
|
||||
}
|
||||
|
||||
struct LocationPrivacyExample: UIViewRepresentable {
|
||||
@Binding var mapView: MLNMapView
|
||||
@Binding var locationAccuracy: LocationAccuracyState
|
||||
struct PrivacyExampleRepresentable: UIViewRepresentable {
|
||||
@ObservedObject var mapViewModel: PrivacyExampleViewModel
|
||||
|
||||
func makeCoordinator() -> Coordinator {
|
||||
Coordinator(mapView: $mapView, locationAccuracy: $locationAccuracy)
|
||||
func makeCoordinator() -> PrivacyExampleCoordinator {
|
||||
PrivacyExampleCoordinator(mapViewModel: mapViewModel)
|
||||
}
|
||||
|
||||
func makeUIView(context: Context) -> MLNMapView {
|
||||
let mapView = MLNMapView()
|
||||
mapView.showsUserLocation = true
|
||||
mapView.delegate = context.coordinator
|
||||
|
||||
mapView.delegate = context.coordinator
|
||||
mapView.showsUserLocation = true
|
||||
return mapView
|
||||
}
|
||||
|
||||
func updateUIView(_: MLNMapView, context _: Context) {}
|
||||
func updateUIView(_ mapView: MLNMapView, context _: Context) {
|
||||
if mapViewModel.showTemporaryLocationAuthorization {
|
||||
let purposeKey = "MLNAccuracyAuthorizationDescription"
|
||||
mapView.locationManager.requestTemporaryFullAccuracyAuthorization?(withPurposeKey: purposeKey)
|
||||
DispatchQueue.main.async {
|
||||
mapViewModel.showTemporaryLocationAuthorization = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct LocationPrivacyExampleView: View {
|
||||
@State private var mapView = MLNMapView()
|
||||
@State var locationAccuracy: LocationAccuracyState = .unknown
|
||||
@StateObject private var viewModel = PrivacyExampleViewModel()
|
||||
|
||||
var body: some View {
|
||||
VStack {
|
||||
LocationPrivacyExample(mapView: $mapView, locationAccuracy: $locationAccuracy)
|
||||
PrivacyExampleRepresentable(mapViewModel: viewModel)
|
||||
.edgesIgnoringSafeArea(.all)
|
||||
|
||||
if locationAccuracy == LocationAccuracyState.reducedAccuracy {
|
||||
if viewModel.locationAccuracy == LocationAccuracyState.reducedAccuracy {
|
||||
Button("Request Precise Location") {
|
||||
handleButtonPress(mapView: mapView)
|
||||
viewModel.showTemporaryLocationAuthorization.toggle()
|
||||
}
|
||||
.padding()
|
||||
.background(Color.blue)
|
||||
@@ -86,17 +96,6 @@ struct LocationPrivacyExampleView: View {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func handleButtonPress(mapView: MLNMapView) {
|
||||
print("Requesting precice location")
|
||||
switch locationAccuracy {
|
||||
case .reducedAccuracy:
|
||||
let purposeKey = "MLNAccuracyAuthorizationDescription"
|
||||
mapView.locationManager.requestTemporaryFullAccuracyAuthorization!(withPurposeKey: purposeKey)
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// #-end-example-code
|
||||
|
||||
Reference in New Issue
Block a user