iosaugmented-realityarkitcompass-geolocation

Rotate SCNNode to North without permission for location tracking


We would like to rotate a SCNNode so as to match with the North direction.

Arkit allows to align its z-axis with the North but this requires user permission for location tracking, and we don't want to ask for it. see gravityandheading

As a consequence, we're trying to use the magnetic field property of CMDeviceMotion. But we have no idea how to do that. There are probably some matrix calculations, but we don't master them for the time being. see magneticfield

Any help would be highly appreciated! thanks!


Solution

  • You cannot use Location Tracking without user's permission.

    Here are some tips on how to setup it properly for AR apps and NON-AR apps:

    In iOS 11 and higher here's what you need to do to get location working:

    Use these two Property List Keys in info.plist file. They are used since iOS 11 was released:

    <key>NSLocationWhenInUseUsageDescription</key>
      <string>App needs an access to your location (in foreground)</string>
    
    <key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
      <string>App needs an access to your location (in background)</string>
    

    And these two Property List Keys were deprecated earlier (DON'T USE THESE KEYS):

        <key>NSLocationUsageDescription</key>
          <string>App needs an access to your location (in background)</string>
    
        <key>NSLocationAlwaysUsageDescription</key>
          <true/>
    

    Here's how your code for AR apps should look like:

    let configuration = ARWorldTrackingConfiguration()
    
    func session(_ session: ARSession, didFailWithError error: Error) {
    
        switch error.code {
        case 101:
            configuration.worldAlignment = .gravity
            restartSession()
        default:
            configuration.worldAlignment = .gravityAndHeading
            restartSession()
        }
    }   
    func restartSession() {    
        self.sceneView.session.pause()
        self.sceneView.session.run(configuration, 
                               options: [.resetTracking, .removeExistingAnchors])
    }
    

    For NON-AR apps use these two instance methods: requestAlwaysAuthorization() (requests permission to use location services whenever the app is running) and requestWhenInUseAuthorization() (requests permission to use location services while the app is in the foreground).

    Here's how your code for NON-AR apps should look like:

    import CoreLocation
    
    class ViewController: UIViewController, CLLocationManagerDelegate {
    
        var locationManager = CLLocationManager()
    
        func updateMyLocation() {
    
            locationManager.delegate = self
            locationManager.desiredAccuracy = kCLLocationAccuracyThreeKilometers
    
            if locationManager.respondsToSelector(#selector(CLLocationManager.requestWhenInUseAuthorization)) {
                locationManager.requestWhenInUseAuthorization()
            } else {
                locationManager.startUpdatingLocation()
            }
        }
    }
    

    Also, you can request Always Authorization:

    let locationManager = CLLocationManager()   
    
    func enableLocationServices() {
    
        locationManager.delegate = self
            
        switch CLLocationManager.authorizationStatus() {
    
            case .notDetermined:
                // Request when-in-use authorization initially
                locationManager.requestWhenInUseAuthorization()
                break
                
            case .restricted, .denied:
                // Disable location features
                disableMyLocationBasedFeatures()
                break
                
            case .authorizedWhenInUse:
                // Enable basic location features
                enableMyWhenInUseFeatures()
                break
                
            case .authorizedAlways:
                // Enable any of your app's location features
                enableMyAlwaysFeatures()
                break
        }      
    }
    

    Hope this helps.