Is that possible to receive Location even when app is not running in Swift - ios

I am making an app that trace all the trips made by a user.
The app works perfectly when it is in background or in foreground but I am looking for a way to get it worked even if the app is not running.
I followed exactly this answer : Receiving Location even when app is not running in Swift
But this doesn't work as expected.
I have two property in my AppDelegate
var lastLocation:CLLocation?
//And a location manager
var locationManager = CLLocationManager()
In my DidFinishLaunchingWithOptions I configure the CLLocationManager
locationManager.delegate = self
locationManager.requestAlwaysAuthorization()
locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters
In my applicationWillTerminate :
lastLocation = HomeController.locations.last
createRegion(location: lastLocation)
Here is the rest :
func createRegion(location:CLLocation?) {
if CLLocationManager.isMonitoringAvailable(for: CLCircularRegion.self) {
let coordinate = CLLocationCoordinate2DMake((location?.coordinate.latitude)!, (location?.coordinate.longitude)!)
let regionRadius = 50.0
let region = CLCircularRegion(center: CLLocationCoordinate2D(
latitude: coordinate.latitude,
longitude: coordinate.longitude),
radius: regionRadius,
identifier: "aabb")
region.notifyOnExit = true
region.notifyOnEntry = true
//Send your fetched location to server
UserDefaults.standard.set(UserDefaults.standard.integer(forKey: "nbLocNotrunning") + 1, forKey: "nbLocNotrunning")
//Stop your location manager for updating location and start regionMonitoring
self.locationManager.stopUpdatingLocation()
self.locationManager.startMonitoring(for: region)
} else {
print("System can't track regions")
}
}
func locationManager(_ manager: CLLocationManager, didEnterRegion region: CLRegion) {
print("Entered Region")
}
func locationManager(_ manager: CLLocationManager, didExitRegion region: CLRegion) {
print("Exited Region")
locationManager.stopMonitoring(for: region)
//Start location manager and fetch current location
locationManager.startUpdatingLocation()
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
if UIApplication.shared.applicationState != .inactive {
} else {
//App is in BG/ Killed or suspended state
//send location to server
// create a New Region with current fetched location
let location = locations.last
lastLocation = location
//Make region and again the same cycle continues.
self.createRegion(location: lastLocation)
}
}
I wonder where the mistake can come from and why I never enter in the function didExitRegion.
For now, I just increment a variable in UserDefaut "nbLocNotrunning" in order to know how many times I leave a region but this number never increase when I stop my app.

Related

Get user location in all states (i.e backgroud, foreground or app terminated or killed)

I am developing ios app using Swift4 and in that, I need to fetch user location continuously after few seconds in all states i.e. background, foreground or even app is killed. I tried a lot of codes available but none seems work.
Capture location in all states
For app delegate :
if ((launchOptions?[UIApplication.LaunchOptionsKey.location]) != nil) {
print ("abhishek testing termination")
fromTerminated = true
locationManager.requestAlwaysAuthorization()
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBestForNavigation
locationManager.allowsBackgroundLocationUpdates = true
locationManager.startUpdatingLocation()
locationManager.startMonitoringSignificantLocationChanges() //THIS IS WHERE THE MAGIC HAPPENS
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
if(fromTerminated)
{
let newlocation : CLLocation = locations.last!
let locValue:CLLocationCoordinate2D = newlocation.coordinate
// let theaccuracy : CLLocationAccuracy = newlocation.horizontalAccuracy
print ("background location\(locValue.latitude)")
print ("terminated location\(locValue.longitude)")
// self.postdatatoserver()
}
}

How to check if user's current location is inside a location circle

i want to check if user if inside a vicinity. For example i have specified a radius of 50 meters around current of location of user. Let's say if user is moving, now i want to check if user in inside 50 meter radius or not. Here is my code
override func viewDidLoad() {
super.viewDidLoad()
locationManager.startMonitoringVisits()
locationManager.delegate = self
locationManager.distanceFilter = 1
locationManager.allowsBackgroundLocationUpdates = true
locationManager.startUpdatingLocation()
}
Here is code for checking distance
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
guard let location = locations.first else {
return
}
let officeLocation = CLLocationCoordinate2D.init(latitude: 31.471303736482234, longitude: 74.27275174139386)
let circle = MKCircle(center: officeLocation, radius: 50 as CLLocationDistance)
if location.distance(from: officeLocation) > circle.radius {
self.newVisitReceived(des: "YOU ARE OUT OF OFFICE")
}
else{
self.newVisitReceived(des: "YOU ARE IN OFFICE")
}
}
Even if i don't move this code sends notification "YOU ARE OUT".
I would solve this with Geofences...
You have to specify a coordinate center & radius where you want to listen to the user when he goes inside/outside from your geofence.
override func viewDidLoad() {
super.viewDidLoad()
let locationManager = CLLocationManager()
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBestForNavigation
locationManager.allowsBackgroundLocationUpdates = true
locationManager.requestAlwaysAuthorization()
}
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
if status == .authorizedAlways || status == .authorizedWhenInUse {
// CLLocationCoordinate2D; You have to put the coordinate that you want to listen
let region = CLCircularRegion(center: CLLocationCoordinate2D(latitude: 324234, longitude: 23423), radius: 50, identifier: "Ur ID")
region.notifyOnExit = true
region.notifyOnEntry = true
manager.startMonitoring(for: region)
}
}
func locationManager(_ manager: CLLocationManager, didExitRegion region: CLRegion) {
// User has exited from ur regiom
}
func locationManager(_ manager: CLLocationManager, didEnterRegion region: CLRegion) {
// User has exited from ur region
}
I hope this will be useful
The general problem with location services is that the measurements vary in accuracy, depending on a lot of factors. How would your like your code to behave if the user is standing right on the 50 meter boundary? Your current code would then flip back and forth randomly between 'in office' and 'out of office', if the accuracy is bad.
I think accuracy of a GPS is actually more than 4 meters under the best conditions, so a distanceFilter of 1 might not be appropriate.
I guess you would need some state in your app that tracks when the user was last seen inside the 50 meter radius, and also some grace period before updating that variable again, to avoid 'flickering'.

Location does not update after application comes back from background

I am building an app where I want to keep track of updated user location whenever the app comes back from background.
I wrote my location tracking code in AppDelegate's didFinishLaunchingWithOptions method
//Core Location Administration
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.distanceFilter = 70
locationManager.requestAlwaysAuthorization()
locationManager.pausesLocationUpdatesAutomatically = false
locationManager.startMonitoringVisits()
locationManager.delegate = self
Since I was not able to validate Visits, I added the standard location tracking too
locationManager.allowsBackgroundLocationUpdates = true
locationManager.startUpdatingLocation()
I created CLLocationManagerDelegate block and added the following code
extension AppDelegate: CLLocationManagerDelegate {
func locationManager(_ manager: CLLocationManager, didVisit visit: CLVisit) {
let clLocation = CLLocation(latitude: visit.coordinate.latitude, longitude: visit.coordinate.longitude)
// Get location description
AppDelegate.geoCoder.reverseGeocodeLocation(clLocation) { placemarks, _ in
if let place = placemarks?.first {
let description = "\(place)"
self.newVisitReceived(visit, description: description)
}
}
}
func newVisitReceived(_ visit: CLVisit, description: String) {
let location = Location(visit: visit, descriptionString: description)
LErrorHandler.shared.logInfo("\(location.latitude), \(location.longitude)")
UserModal.shared.setUserLocation(location)
UserModal.shared.userLocationUpdated = Date()
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
guard let location = locations.first else {
return
}
locationManager.stopUpdatingLocation()
let uL = Location(location:location.coordinate, descriptionString: "")
LErrorHandler.shared.logInfo("\(uL.latitude), \(uL.longitude)")
UserModal.shared.setUserLocation(uL)
UserModal.shared.userLocationUpdated = Date()
}
}
I added this code to begin location tracking when the app comes to foreground
func applicationWillEnterForeground(_ application: UIApplication) {
// Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
locationManager.startUpdatingLocation()
}
If I am on a ViewController and the application goes back to the background, it does not refresh the location when the app comes to foreground.
Can someone suggest a better way to do this?
You should write code for location in didbecomeActive method of App delegate not in didFinishLaunchingWithOptions. Try this hope it will help.

How to update background location of user within specific time interval using swift 4 iOS?

I am working on an app. where I am updating a background location of a user. and I want to update that location within a specific time interval. I have searched a lot. but did not get any solution. please help me out. Here my code below. Thanks in advance.
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation])
{
let userLocation :CLLocation = locations[0] as CLLocation
print("user latitude = \(userLocation.coordinate.latitude)")
print("user longitude = \(userLocation.coordinate.longitude)")
self.labelLat.text = "\(userLocation.coordinate.latitude)"
self.labelLongi.text = "\(userLocation.coordinate.longitude)"
DataBaseFunctions.sharedInstance.saveUserDetail(dataToInsert: userLocation)
}
This is in Swift-NOW,
You can use Timer for didUpdateLocation with
locationManager = CLLocationManager()
locationManager.desiredAccuracy = kCLLocationAccuracyBestForNavigation
locationManager.delegate = self
locationManager.startUpdatingLocation()
var timer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(self.updateLocation), userInfo: nil, repeats: true)
func updateLocation() {
let location: CLLocation = locationManager.location
// Your code
}
// MARK: - CLLocationManagerDelegate
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
// Your code
}

Why does logging backgroundTimeRemaining show a wrong/big number even though app was moved to background?

I'm logging my UIApplication.shared.backgroundTimeRemaining but the number is huge. It's almost 200 digits.
This is how I'm logging it.
os_log("Lat: %f | Long: %f | RemainingTime: %f ", log: log, type: .default, location.coordinate.latitude, location.coordinate.longitude, UIApplication.shared.backgroundTimeRemaining)
I thought there is something wrong with the format of my logging so I also tried placing a breakpoint and printing it but still the number that it logs is the same huge number. I also looked into this question which has a fair explanation, that is if your app is in foreground then the time would be that huge. But I still see this number even if there has been 5 minutes since I've moved the app to background.
A sample number I get for my remainingTime is:
179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.000000
Entire code:
import UIKit
import CoreLocation
import os.log
import MapKit
class ViewController: UIViewController, CLLocationManagerDelegate{
lazy var locationManager : CLLocationManager = {
var manager = CLLocationManager()
manager.delegate = self
manager.desiredAccuracy = kCLLocationAccuracyBestForNavigation
manager.distanceFilter = 1
manager.pausesLocationUpdatesAutomatically = true
manager.allowsBackgroundLocationUpdates = true
manager.requestAlwaysAuthorization()
manager.startUpdatingLocation()
return manager
}()
var lastLocation : CLLocation?
var mapView : MKMapView?
let log = OSLog(subsystem: "XYZ.LocationAppSubSystem", category: "dumbo")
override func viewDidLoad() {
super.viewDidLoad()
if locationManager.location != nil{
}else {
DispatchQueue.main.async {
self.locationManager.startUpdatingLocation()
}
}
os_log("view was Loaded", log: log, type: .error)
mapView = MKMapView(frame: UIScreen.main.bounds)
mapView?.showsUserLocation = true
view.addSubview(mapView!)
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
guard let location = locations.last else {
return
}
lastLocation = location
// let date = Date().description(with: Locale.current)
os_log("Lat: %{public}f | Long: %{private}f | RemainingTime: %{public}f ", log: log, type: .default, location.coordinate.latitude, location.coordinate.longitude, UIApplication.shared.backgroundTimeRemaining)
}
func locationManagerDidPauseLocationUpdates(_ manager: CLLocationManager) {
os_log("locationManager was paused", log: log)
let location = lastLocation
os_log("Lat: %{public}f | Long: %{private}f | RemainingTime: %{public}f ", log: log, type: .default, (location?.coordinate.latitude)!, (location?.coordinate.longitude)!, UIApplication.shared.backgroundTimeRemaining)
}
func locationManager(_ manager: CLLocationManager, didExitRegion region: CLRegion) {
os_log("Region was exited", log: log)
}
func createRegion(location: CLLocation) {
let radius = 3.0
let region = CLCircularRegion(center: location.coordinate, radius: radius, identifier: "didPauseLocationUpdates")
region.notifyOnExit = true
region.notifyOnEntry = false
locationManager.startMonitoring(for: region)
}
func locationManager(_ manager: CLLocationManager, monitoringDidFailFor region: CLRegion?, withError error: Error) {
if region?.identifier == "didPauseLocationUpdates"{
os_log("Main Region was Failed to be created", log: log)
}else{
os_log("Other regions were checked ", log: log)
}
}
}
It is because even though the app is in foreground or even closed, when a didEnterRegion, didExitRegion (apparently didUpdateLocations as well) event occurs, the app gets 10 seconds. In those 10 seconds, the backgroundTimeRemaining value will show the same as in foreground. However, if you register for a background task, the backgroundTimeRemaining will show the real background time remaining after those 10 seconds have passed.
In iOS, regions associated with your app are tracked at all times, including when the app isn’t running. If a region boundary is crossed while an app isn’t running, that app is relaunched into the background to handle the event. Similarly, if the app is suspended when the event occurs, it’s woken up and given a short amount of time (around 10 seconds) to handle the event. When necessary, an app can request more background execution time using the beginBackgroundTaskWithExpirationHandler: method of the UIApplication class.
https://developer.apple.com/library/content/documentation/UserExperience/Conceptual/LocationAwarenessPG/RegionMonitoring/RegionMonitoring.html

Resources