LaunchOptions.locationKey in didFinishLaunchingWithOptions always nil after Application will be waked up from location

I did search on the internet, found some people who have the same problem but no one did get any solution yet... So I hope anyone here is the G I am searching for..

1 - I did enable "Location updates" and "background fetch" in Background Modes.


https://i.stack.imgur.com/eNW4K.png


2 - I did call

locationManager.startMonitoringSignificantLocationChanges()
on the right place, I checked this on the way I write and string into my Firebase Database when the app will be waked up after terminating.

3 - I am checking on the right way if there is a location key in launchOptions just like Apple on their documentation, see:

https://developer.apple.com/documentation/uikit/app_and_environment/responding_to_the_launch_of_your_app


https://i.stack.imgur.com/cW3dx.png


So why is my launchOptions nil? I cannot understand why this happens... Because the App is getting waked up, the mistake cannot be on my locationManager handling..

I found this on stackoverflow but my launchOptions are nil so the code is not getting inside the if...

https://stackoverflow.com/questions/34527972/location-update-even-when-app-is-killed-terminated


Please help.


When my app launches the first time after installing, I initialize the locationManger. When I use the switchToogle for enable/disable location tracking, I start/stop the locationManger updating location.

When my app goes in background, I restart the location updating.

When my app was killed by user, the app wakes up and starts into app delegate method didFinishLaunchWithOptions.


I hope its clear how my code works, if not just tell me!


LocationMangerClass:


import Foundation

import CoreLocation

import Firebase



class LocationManagerClass : NSObject {


static var locationManager: CLLocationManager!


override init() {

print("init...")

LocationManagerClass.locationManager = CLLocationManager()

}


func getLocationManger() -> CLLocationManager {

if LocationManagerClass.locationManager == nil {

LocationManagerClass.locationManager = CLLocationManager()

}

return LocationManagerClass.locationManager

}


func startLocationTracking() {

print("start...")

if LocationManagerClass.locationManager != nil {

LocationManagerClass.locationManager.stopMonitoringSignificantLocationChanges()

LocationManagerClass.locationManager.stopUpdatingLocation()

}

LocationManagerClass.locationManager = CLLocationManager()

LocationManagerClass.locationManager.delegate = self

LocationManagerClass.locationManager.desiredAccuracy = kCLLocationAccuracyBest

LocationManagerClass.locationManager.activityType = .otherNavigation

LocationManagerClass.locationManager.requestAlwaysAuthorization()

LocationManagerClass.locationManager.pausesLocationUpdatesAutomatically = false

LocationManagerClass.locationManager.startMonitoringSignificantLocationChanges()

LocationManagerClass.locationManager.startUpdatingLocation()

}


func restartLocationTracking() {

LocationManagerClass.locationManager.stopMonitoringSignificantLocationChanges()

LocationManagerClass.locationManager.stopUpdatingLocation()

LocationManagerClass.locationManager.requestAlwaysAuthorization()

LocationManagerClass.locationManager.startMonitoringSignificantLocationChanges()

LocationManagerClass.locationManager.startUpdatingLocation()

}


func stopLocationTracking() {

LocationManagerClass.locationManager.stopUpdatingLocation()

LocationManagerClass.locationManager.stopMonitoringSignificantLocationChanges()

}

}



extension LocationManagerClass : CLLocationManagerDelegate {


func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {

if let location = locations.last {

let calender = NSCalendar.current

let date = NSDate()

let components = calender.dateComponents([.hour, .minute , .second], from: date as Date)

if components.second! % 14 == 0 || components.second! % 15 == 0 || components.second! % 16 == 0 {

let coordinate = location.coordinate

let lati: String = String(format:"%f", coordinate.latitude)

let longi: String = String(format:"%f", coordinate.longitude)

let appState = UIApplication.shared.applicationState

if appState == .active {

writeToDataBase(appState: "active", lat: lati, long: longi)

} else if appState == .background {

writeToDataBase(appState: "background", lat: lati, long: longi)

} else if appState == .inactive {

writeToDataBase(appState: "inactive", lat: lati, long: longi)

}

}

}

}


func writeToDataBase(appState: String, lat: String, long: String) {

let calender = NSCalendar.current

let date = NSDate()

let components = calender.dateComponents([.hour, .minute , .second], from: date as Date)

Database.database().reference().child("realDevice").child("locations").child("appStates").child(appState).childByAutoId().setValue("\(components.hour!):\(components.minute!):\(components.second!)Uhr lat: \(lat) : long: \(long)")

}


func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {

// checkLocationAuthorization()

}

}


Start Tracking Controller:


import UIKit

import CoreLocation

import Firebase



class TrackingViewController: UIViewController {


@IBOutlet weak var locationTrackingSwitch: UISwitch!


var locationManager: LocationManagerClass!

var appDelegate = UIApplication.shared.delegate as! AppDelegate


override func viewDidLoad() {

super.viewDidLoad()

setupView()

locationManager = appDelegate.locationManager

let notificationCenter = NotificationCenter.default

notificationCenter.addObserver(self, selector: #selector(appMovedToBackground), name: UIApplication.willResignActiveNotification, object: nil)

notificationCenter.addObserver(self, selector: #selector(appMovedToForeGround), name: UIApplication.willEnterForegroundNotification, object: nil)



}


@objc func appMovedToBackground() {

Database.database().reference().child("realDevice").child("state3").setValue("background")

guard _ = UserDefaults.standard.value(forKey: "userEnabledLocationTracking") as? Bool else {locationManager.stopLocationTracking(); return}

locationManager.restartLocationTracking()

}



@objc func appMovedToForeGround() {

Database.database().reference().child("realDevice").child("state1").setValue("foreground")

}


@IBAction func locationTrackingSwitchUsed(_ sender: Any) {

if locationTrackingSwitch.isOn == true {

locationManager.startLocationTracking()

UserDefaults.standard.set(true, forKey: "userEnabledLocationTracking")

UserDefaults.standard.synchronize()

} else if locationTrackingSwitch.isOn == false {

locationManager.stopLocationTracking()

UserDefaults.standard.set(false, forKey: "userEnabledLocationTracking")

UserDefaults.standard.synchronize()

}

}

}


and my App Delegate:


import UIKit

import CoreLocation

import Firebase



@UIApplicationMain

class AppDelegate: UIResponder, UIApplicationDelegate {



var window: UIWindow?

var locationManager = LocationManagerClass()



var ref: DatabaseReference!


override init() {

FirebaseApp.configure()

ref = Database.database().reference()

}



func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {



if let keys = launchOptions?.keys {

Database.database().reference().child("realDevice").child("state01").setValue("launchOptionsAreNotNil")

if keys.contains(.location) {

locationManager.startLocationTracking()

Database.database().reference().child("realDevice").child("state01").setValue("launchOptionsLocationKeyIsNotNil")

}

Database.database().reference().child("realDevice").child("state01").setValue("launchOptionsLocationKeyIsNil")

}

//work around to get locationTracking started because launchOptions are nil

guard let isLocationEnabled = UserDefaults.standard.value(forKey: "userEnabledLocationTracking") as? Bool else {locationManager.stopLocationTracking(); return true}

if isLocationEnabled {

locationManager.startLocationTracking()

}

return true

}


func applicationWillTerminate(_ application: UIApplication) {

Database.database().reference().child("realDevice").child("state2").setValue("terminating")

}


// MARK: UISceneSession Lifecycle



@available(iOS 13.0, *)

func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {

// Called when a new scene session is being created.

// Use this method to select a configuration to create the new scene with.

return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)

}



@available(iOS 13.0, *)

func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>) {

// Called when the user discards a scene session.

// If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.

// Use this method to release any resources that were specific to the discarded scenes, as they will not return.

}





}

Post not yet marked as solved Up vote post of fuhrthom Down vote post of fuhrthom
2k views

Replies

Before explaining how you did everything OK 😉, could you first clearly explain what you did ? That would help a lot.

It would also be better to show the code and not paraphrase it.


Thanks for refining your question (may be it is all in the title, which is not the best place and not enough anyway).

Thank you, for your answer. I updated the post, I hope you have now all infos you needed. If not, just let me know 🙂

Hi, did you find a solution for this? I have the same issue. The AppDelegate didFinishLaunchingWithOptions is called when the app is woken up in killed state due to a location update. But the launch option location key is nil.

launchOptions is always nil in app delegate if using scene delegates

scene delegate launchOptions does not provide access to the significant location launch reason

here is a workaround: https://stackoverflow.com/a/78228563/259521