skip to Main Content

So I am encountering a unique issue with my SwiftUI app that requires always access to the user’s location. The permissions prompt is not showing when called (testing is being done on a physical device on iOS 17.0.3, and app is uninstalled before being ran again). The request authorization code is shown below, and all print statements show to have fired off the request once the user has signed in, but the authorization status is zero once that is completed. The info.plist values are also all correct.

.onReceive(authViewModel.$isSignedIn) { isSignedIn in
            if isSignedIn {
                locationManager.requestPermission()
            }
        }

Location manager:

func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
    DispatchQueue.main.async {
        self.authorizationStatus = status
    }
    print("Authorization status changed: (status.rawValue)")

    switch status {
    case .notDetermined:
        print("Permission not determined, requesting permission.")
        locationManager.requestWhenInUseAuthorization()
    case .restricted, .denied:
        print("Location access restricted or denied.")
    case .authorizedAlways, .authorizedWhenInUse:
        print("Location access granted.")
        // Optionally setup geofences here if needed
    @unknown default:
        fatalError("Unhandled authorization status: (status.rawValue)")
    }
}

Request permission method:

class LocationManager: NSObject, ObservableObject, CLLocationManagerDelegate {
    private var locationManager = CLLocationManager()
    @Published var authorizationStatus: CLAuthorizationStatus

//..........Other methods

    func requestPermission() {
        DispatchQueue.main.async {
            self.locationManager.requestWhenInUseAuthorization()
        }
    }

Main app file:

struct myApp: App {
    @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
    @StateObject var authViewModel = UserAuthViewModel()
    @StateObject var barViewModel = BarViewModel()
    @StateObject private var locationManager = LocationManager.shared

Console outputs:

Authorization status changed: 0

Permission not determined, requesting permission.

Starting location request permissions

Requesting location permission.

Location permissions requested

I tried adding a delay to ensure the prompt was being called when the app was in the foreground, and all UI components had loaded.

.onAppear {
        DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
            barViewModel.fetchData()
            print("Starting location request permissions")
            self.requestLocationPermission()
            print("Location permissions requested")
        }
    }

Even when I modify my app delegate to call the request method at launch it still does not prompt the user:

class AppDelegate: NSObject, UIApplicationDelegate {
    
    var locationManager: LocationManager?
    
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        FirebaseApp.configure()
        
        // Setup location manager
        self.locationManager = LocationManager.shared
        self.locationManager?.requestPermission() // Request permission as soon as the app launches
        
        return true
    }
}

2

Answers


  1. Chosen as BEST ANSWER

    Figured it out! Added the keys to custom target properties, and upon further review discovered that my info.plist file was actually assigned to no target which was likely the issue.


  2. You can’t use DispatchQueue in SwiftUI because the View structs are just values that have no lifetime, the callback will be on an old value that has been and gone.

    View model class terminology doesn’t make sense in SwiftUI because it is the job of the View struct to hold the view data that is used to later build a UI.

    onReceive is a very old modifier and has real use nowadays since we have onChange.

    Make another controller class AuthorizationController that has a location manager delegate only for authorisation. request permission in this classs init because that happens at on appear automatically. Make an @Published var isAuthorized bool. In the Views body, make an if that checks this bool and then init another View that has its own state object LocationController that has another location manager and has the location update delegate methods. This way if authorisation changes, this View will be destroyed and along with it the location delegate state object.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search