Zorgan
Zorgan

Reputation: 9153

Location permission dialog fires before I call requestWhenInUseAuthorization()

When my view appears:

struct TurnOnLocation: View {
    
    var body: some View {
        ZStack {
            Color.orange
                .edgesIgnoringSafeArea(.all)
            VStack {
                Spacer()
                Image(systemName: "globe")
                    .resizable()
                    .aspectRatio(contentMode: .fill)
                    .foregroundColor(.white)
                Spacer()

                Button(action: {
                    let locationManager = LocationManager()
                    locationManager.locationManager.requestWhenInUseAuthorization()
                    locationManager.locationManager.startUpdatingLocation()
                }) {
                    Text("Turn on location")
                }.foregroundColor(Color.black)
                .font(Font.system(size:22))
                .padding(20)
                .background(
                    Rectangle()
                        .fill(Color.white)
                        .border(Color.white, width:1)
                )
                .cornerRadius(50)
                .shadow(radius: 10)
                .padding(50)
            }
        }
    }
}

It asks the user for location before the Button is tapped. Why is this? I've added my model below to be fired when it's initialised (which should be when the button is clicked).

class LocationManager: NSObject, ObservableObject {
    @Published var locationStatus: CLAuthorizationStatus? = CLAuthorizationStatus.notDetermined
    @Published var locationPermissionStatus = PermissionStatus.unknown
    @Published var location: CLLocation?
    let locationManager = CLLocationManager()
    
    override init(){
        super.init()
        self.locationManager.delegate = self
        self.locationManager.desiredAccuracy = kCLLocationAccuracyBest
        self.locationManager.requestWhenInUseAuthorization()
        self.locationManager.startUpdatingLocation()
    }
    
    enum PermissionStatus {
        case unknown
        case authorizedWhenInUse
        case authorizedAlways
        case restricted
        case denied
    }
    
    func getLocation() -> CLLocationCoordinate2D? {
        return self.location?.coordinate
    }
    
}

Any idea how I can ensure the dialog only shows after the button is clicked?

EDIT:

I have updated the code:

Button(action: {
    let locationManager = LocationManager()
    locationManager.requestPermission()
})

Model

class LocationManager: NSObject, ObservableObject , CLLocationManagerDelegate {
    @Published var locationStatus: CLAuthorizationStatus? = CLAuthorizationStatus.notDetermined
    @Published var locationPermissionStatus = PermissionStatus.unknown
    @Published var location: CLLocation?
    let locationManager = CLLocationManager()
    
    override init(){
        super.init()
        self.locationManager.delegate = self
        self.locationManager.desiredAccuracy = kCLLocationAccuracyBest
    }
    
    func requestPermission(){
        self.locationManager.requestWhenInUseAuthorization()
    }
    
    func locationManager(_ manager: CLLocationManager,
                         didChangeAuthorization status: CLAuthorizationStatus) {   switch status {
          case .restricted, .denied:
             // Disable your app's location features
            print("restricted")
             break
                
          case .authorizedWhenInUse:
             // Enable your app's location features.
            print("authorizedWhenInUse")
            self.locationManager.startUpdatingLocation()
             break
                
          case .authorizedAlways:
             // Enable or prepare your app's location features that can run any time.
            print("authorizedAlways")
            self.locationManager.startUpdatingLocation()
             break
                
          case .notDetermined:
            print("notDetermined")
             break
       }
    }

However, when I click the button, the dialog asking for location permissions disappears after one second. Any idea why?

Upvotes: 2

Views: 441

Answers (3)

phnmnn
phnmnn

Reputation: 13260

To fix disappearing after 1 sec, try to init location manage like here:

struct TurnOnLocation: View {
    let locationManager = LocationManager() <- try to init location manager here

    var body: some View {
        ZStack {
            Color.orange
                .edgesIgnoringSafeArea(.all)
            VStack {
                Spacer()
                Image(systemName: "globe")
                    .resizable()
                    .aspectRatio(contentMode: .fill)
                    .foregroundColor(.white)
                Spacer()

                Button(action: {   
                 locationManager.locationManager.requestWhenInUseAuthorization()
                    locationManager.locationManager.startUpdatingLocation()
                }) {
                    Text("Turn on location")
                }.foregroundColor(Color.black)
                .font(Font.system(size:22))
                .padding(20)
                .background(
                    Rectangle()
                        .fill(Color.white)
                        .border(Color.white, width:1)
                )
                .cornerRadius(50)
                .shadow(radius: 10)
                .padding(50)
            }
        }
    }
}

Upvotes: 0

Mohammad Rahchamani
Mohammad Rahchamani

Reputation: 5220

You are requesting authorization in your LocationManager init method. you should remove that code from init.

class LocationManager: NSObject, ObservableObject {
    @Published var locationStatus: CLAuthorizationStatus? = CLAuthorizationStatus.notDetermined
    @Published var locationPermissionStatus = PermissionStatus.unknown
    @Published var location: CLLocation?
    let locationManager = CLLocationManager()
    
    override init(){
        super.init()
        self.locationManager.delegate = self
        self.locationManager.desiredAccuracy = kCLLocationAccuracyBest
        self.locationManager.requestWhenInUseAuthorization()
        self.locationManager.startUpdatingLocation()
    }

    func requestAuthorization() {
        self.locationManager.requestWhenInUseAuthorization()
    }

    func startUpdate() {
        self.locationManager.startUpdatingLocation()
    }
    
    enum PermissionStatus {
        case unknown
        case authorizedWhenInUse
        case authorizedAlways
        case restricted
        case denied
    }
    
    func getLocation() -> CLLocationCoordinate2D? {
        return self.location?.coordinate
    }
    
}

and in your view:

struct TurnOnLocation: View {
    
    var body: some View {
        ZStack {
            Color.orange
                .edgesIgnoringSafeArea(.all)
            VStack {
                Spacer()
                Image(systemName: "globe")
                    .resizable()
                    .aspectRatio(contentMode: .fill)
                    .foregroundColor(.white)
                Spacer()

                Button(action: {
                    let locationManager = LocationManager()
                    locationManager.requestAuthorization()
                    // you should check for authorization result first
                    locationManager.startUpdatingLocation()
                }) {
                    Text("Turn on location")
                }.foregroundColor(Color.black)
                .font(Font.system(size:22))
                .padding(20)
                .background(
                    Rectangle()
                        .fill(Color.white)
                        .border(Color.white, width:1)
                )
                .cornerRadius(50)
                .shadow(radius: 10)
                .padding(50)
            }
        }
    }
}

Upvotes: 0

EDUsta
EDUsta

Reputation: 1933

There are two things to fix:

  1. You're already calling requestWhenInUseAuthorization in init, which is why you're seeing the alert when the button is constructed. You should remove requestWhenInUseAuthorization in init.

  2. You should call startUpdatingLocation after you're sure that you have authorization, in LocationManager.

func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
    switch status {
    case .authorizedAlways, .authorizedWhenInUse:
        manager.startUpdatingLocation()
    default:
        print("no auth")
    }
}

Upvotes: 2

Related Questions