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://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.
}
}