How can I know my app is working in background? - ios

I have written this program, it is work when the app is displaying on screen
If the app in background, it stop to print the latitude, when I resume the app, it will start to print again.
I have enabled background modes in xcode, and also checked Location updates, why my app still not running in background?
If the app is running, just the print function does not work in background, how can I know the app is running?
class ViewController: UIViewController,
CLLocationManagerDelegate {
var locationManager: CLLocationManager = CLLocationManager()
var startLocation: CLLocation!
func locationManager(_ manager: CLLocationManager,
didUpdateLocations locations: [CLLocation])
{
let latestLocation: CLLocation = locations[locations.count - 1]
print(latestLocation.coordinate.latitude)
}
override func viewDidLoad() {
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.delegate = self
locationManager.requestWhenInUseAuthorization()
locationManager.startUpdatingLocation()
startLocation = nil
}
}

First, your problem:
locationManager.requestWhenInUseAuthorization()
This requests your location manager to update its location only when your app is in the foreground. You have to change this to:
locationManager.requestAlwaysAuthorization()
If it still doesn't work, make sure your location manager is firing updates at all by adding a print statement in the delegate functions.
Second, how to print stuff in the background:
My favorite way is to log things in UserDefaults, because those persist across app restarts. I will set my print statements as the value to a log key for example. Upon restart I will read the contents of UserDefaults from the log key.

If your app uses location in the background ).you have to set allowsBackgroundLocationUpdates to YES in addition to setting the background mode capability in Info.plist. Otherwise location updates are only delivered in foreground.
if CLLocationManager is first called startUpdatingLocation method, and in the projectname-Info.plist file is added Required Background Modes -> App registers for location updates.

Related

iOS - Check if Developer Mode is ON (Swift)

My App relies on checking user location upon start.
On iOS 16, when the user enables the Developer Mode on Privacy Settings they can simulate any location on the device using a GPX file.
The device actually changes the location of the device and most apps that I use actually think my location is the one on the GPX file.
Is there a way to make sure the location is the actual user location or a simulated one ?
Is there a public API to check if Developer Mode is enabled ?
Is there a way to tell the location comes from the GPX file ?
Even if the Developer mode is turned off it takes restarting the device to pick up the actual location again , so not sure if there is a different and better solution to this.
Thank you.
Apple provides sourceInformation on each CLLocation object returned. Checking the isSimulatedBySoftware parameter will give a boolean response.
Core Location sets isSimulatedBySoftware to true if the system generated the location using on-device software simulation. You can simulate locations by loading GPX files using the Xcode debugger. The default value is false.
The code below is a full functioning example that will turn the screen red if a simulated location is detected. I have confirmed that toggling simulated locations on/off in Xcode will make the screen turn from green to red and back again.
import UIKit
import CoreLocation
class ViewController: UIViewController {
let locationManager = CLLocationManager()
override func viewDidLoad() {
super.viewDidLoad()
// setup location monitoring
locationManager.delegate = self
locationManager.requestWhenInUseAuthorization()
}
}
extension ViewController: CLLocationManagerDelegate {
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
// list of locations that are simulated
let simulatedLocations = locations.filter { $0.sourceInformation?.isSimulatedBySoftware == true }
view.backgroundColor = simulatedLocations.count > 0 ? .red : .green
}
func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {
guard manager.authorizationStatus == .authorizedAlways || manager.authorizationStatus == .authorizedWhenInUse else { return }
locationManager.startUpdatingLocation()
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
print(error.localizedDescription)
}
}

How to track user location in the background swift

I am trying to track the user location in background I have try using when In us with and without always , but every time I minimise the app the location icon disappear I have read in a tutorial it should show blue bar but I am not getting that bar I have also check the background mode for updating the location
map.showsUserLocation = true
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.requestWhenInUseAuthorization()
locationManager.requestAlwaysAuthorization()
locationManager.startUpdatingLocation()
// Do any additional setup after loading the view, typically from a nib.
alwaysAuthorization()
}
func alwaysAuthorization(){
if CLLocationManager.locationServicesEnabled() && CLLocationManager.authorizationStatus() == .authorizedWhenInUse {
locationManager.requestAlwaysAuthorization()
}
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let location = locations.last
let region = MKCoordinateRegion(center: CLLocationCoordinate2D(latitude: location!.coordinate.latitude, longitude: location!.coordinate.longitude), span: MKCoordinateSpan (latitudeDelta: 0.2, longitudeDelta: 0.2))
self.map.region = region
print("location \(location!.coordinate.latitude) and \(location!.coordinate.longitude)")
}
this what I was looking for
locationManager.allowsBackgroundLocationUpdates = true
You can use startMonitoringSignificantLocationChanges() method instead of startUpdatingLocation()
Based on docs:
Apps can expect a notification as soon as the device moves 500 meters or more from its previous notification. It should not expect notifications more frequently than once every five minutes. If the device is able to retrieve data from the network, the location manager is much more likely to deliver notifications in a timely manner.
I have used this method once and tested it. It works well with cellular and wi-fi.
Your question is not clear. Do you have problems running your app in background? Or do you simple expect the blue bar indicating that your app tracks the location in background?
For the latter you need to enable the background location indicator
locationManager.showsBackgroundLocationIndicator = true

Update location in Background

I want to get user location even when the user does not use the app.now i can get location after press home button and application goes to background state, but after a few second location update stoped. And when I'm killing the app location update against stoped. this is my code in app delegate.
let locationManager = CLLocationManager()
var LatitudeGPS = String()
var LongitudeGPS = String()
var speedGPS = String()
var Course = String()
var Altitude = String()
var bgtimer = Timer()
func applicationDidEnterBackground(_ application: UIApplication) {
self.doBackgroundTask()
}
func beginBackgroundUpdateTask() {
backgroundUpdateTask = UIApplication.shared.beginBackgroundTask(expirationHandler: {
self.endBackgroundUpdateTask()
})
}
func endBackgroundUpdateTask() {
UIApplication.shared.endBackgroundTask(self.backgroundUpdateTask)
self.backgroundUpdateTask = UIBackgroundTaskInvalid
}
func doBackgroundTask() {
DispatchQueue.global(qos: .background).async {
self.beginBackgroundUpdateTask()
self.StartupdateLocation()
self.bgtimer = Timer.scheduledTimer(timeInterval: 30, target: self, selector: #selector(self.bgtimer(timer:)), userInfo: nil, repeats: true)
RunLoop.current.add(self.bgtimer, forMode: RunLoopMode.defaultRunLoopMode)
RunLoop.current.run()
self.endBackgroundUpdateTask()
}
}
func bgtimer(timer:Timer!){
print("Fired from Background ************************************")
updateLocation()
}
func StartupdateLocation() {
locationManager.delegate = self
locationManager.startUpdatingLocation()
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.distanceFilter = kCLDistanceFilterNone
locationManager.requestAlwaysAuthorization()
locationManager.allowsBackgroundLocationUpdates = true
locationManager.pausesLocationUpdatesAutomatically = false
}
func updateLocation() {
locationManager.startUpdatingLocation()
locationManager.stopUpdatingLocation()
print("Latitude: \(LatitudeGPS)")
print("Longitude: \(LongitudeGPS)")
print("Speed: \(speedGPS)")
print("Heading: \(Course)")
print("Altitude BG: \(Altitude)")
DispatchQueue.main.async {
print(UIApplication.shared.backgroundTimeRemaining)
}
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
LatitudeGPS = String(format: "%.10f", manager.location!.coordinate.latitude)
LongitudeGPS = String(format: "%.10f", manager.location!.coordinate.longitude)
speedGPS = String(format: "%.3f", manager.location!.speed)
Altitude = String(format: "%.3f", manager.location!.altitude)
Course = String(format: "%.3f", manager.location!.course)
}
}
i think after a few second my application terminated and location update stoped.
I want to after 20 min that application terminated (os or user) stop updating location to keep the battery charge.
where is my problem in location update.
Couple of things to be changed.
Step 1:
Make sure you have enabled location updates background mode in capabilities section of your project as shown below
Step 2:
And when I'm killing the app location update against stoped.
Quoting from apple docs
If you start this service and your app is subsequently terminated, the
system automatically relaunches the app into the background if a new
event arrives. In such a case, the options dictionary passed to the
application(:willFinishLaunchingWithOptions:) and
application(:didFinishLaunchingWithOptions:) methods of your app
delegate contains the key location to indicate that your app was
launched because of a location event. Upon relaunch, you must still
configure a location manager object and call this method to continue
receiving location events. When you restart location services, the
current event is delivered to your delegate immediately. In addition,
the location property of your location manager object is populated
with the most recent location object even before you start location
services.
link : https://developer.apple.com/documentation/corelocation/cllocationmanager/1423531-startmonitoringsignificantlocati
important thing to notice in above statement is
Upon relaunch, you must still, configure a location manager object and
call this method to continue receiving location events.
Meaning, your current location manager will not be of much use and you should create a new one and configure the new instance and call startMonitorSignificantLocationChanges again.
So iOS will send location updates to terminated apps only when you use startMonitoringSignificantLocationChanges.
All that are applicable only if your app was terminated and iOS relaunched it on receiving location update. But if your app is simply in background you need not do any thing on using startMonitorSignificantLocationChanges
on the other hand startUpdatingLocation will work only when app is in background/foreground mode. iOS will stop updating location if your app gets suspended or killed.
If you start this service and your app is suspended, the system stops
the delivery of events until your app starts running again (either in
the foreground or background). If your app is terminated, the delivery
of new location events stops altogether. Therefore, if your app needs
to receive location events while in the background, it must include
the UIBackgroundModes key (with the location value) in its Info.plist
file.
link: https://developer.apple.com/documentation/corelocation/cllocationmanager/1423750-startupdatinglocation
So modify your code
locationManager.startMonitoringSignificantLocationChange()
Ok that was about proper usage of startMonitoringSignificantLocationChanges and startupdatinglocation. Now timer for mistakes in your code.
Mistake 1:
self.bgtimer = Timer.scheduledTimer(timeInterval: 30, target: self, selector: #selector(self.bgtimer(timer:)), userInfo: nil, repeats: true)
Using timer to get timely updates on location. Thats not how it works!!! You cant run Timer forever. Timer stops as soon as your app suspends or gets terminated. Location Manager informs the app when location changes and you should rely on that only. You cant run timer to timely check location updates. It won't run in suspended or terminated state.
Mistake 2:
func updateLocation() {
locationManager.startUpdatingLocation()
locationManager.stopUpdatingLocation()
Why start and stopping update locations in subsequent statements? That does not make much sense.
Mistake 3:
func StartupdateLocation() {
locationManager.delegate = self
locationManager.startUpdatingLocation()
Your StartupdateLocation gets called multiple time and every time you called this method you are repeatedly calling startUpdatingLocation on same instance of location manager. You need not do that! You can call startUpdatingLocation or startMonitoringSignificantLocationChange only once.

Cannot keep GPS active with app in background, or read location with app in foreground or background

The app pertinent to this question is meant to track user location with a high degree of precision, including GPS coordinates and accelerometer readings, for a time period of 30 minutes, even if the user has pressed the sleep button.
To this ends, the plist file and app capabilities settings have been changed to reflect the reason for always on navigation access and to enable background processes for the provision of location based services.
The app does ask the user for GPS permissions when it is run, and if they are granted, the activation of this view controller (the view controller that contains the following code) does cause the GPS/Navigation icon to display on an iPhone.
The problem is, so far none of the four "print" commands seen below result in any printed messages, and so the "newLocation" and "myLocation" variables both do not yield any data. If this code is remotely close to being able to serve the purpose outlined in the first sentence, then the question is "How can it be fixed?". If this is a bad way to accomplish the goal, then a better answer would explain how this should be done.
import UIKit
import CoreMotion
import MapKit
import CoreLocation
class ActiveSession: UIViewController, CLLocationManagerDelegate {
lazy var locationManager: CLLocationManager! = {
let manager = CLLocationManager()
manager.desiredAccuracy = kCLLocationAccuracyBest
manager.delegate = self
return manager
}()
func locationManager(_manager: CLLocationManager!, didUpdateToLocation newLocation: CLLocation!, fromLocation oldLocation: CLLocation!) {
let myLocation:CLLocationCoordinate2D=CLLocationCoordinate2DMake(oldLocation.coordinate.latitude, oldLocation.coordinate.longitude)
print(myLocation)
if UIApplication.shared.applicationState == .active {
print("at least it's active at all")
} else {
print(newLocation)
print("it's active when the app isn't")
}
}
func getPermission () {
locationManager = CLLocationManager()
switch CLLocationManager.authorizationStatus() {
case .denied, .restricted:
return
case .notDetermined:
locationManager!.requestAlwaysAuthorization()
break
case .authorizedAlways, .authorizedWhenInUse:
break
}
}
override func viewDidLoad() {
super.viewDidLoad()
self.getPermission()
locationManager = CLLocationManager()
locationManager!.desiredAccuracy = kCLLocationAccuracyBest
locationManager?.startUpdatingLocation()
}
}
You’re creating new CLLocationManager instances in three places—the lazy locationManager initializer, getPermission, and viewDidLoad—and in the latter two of those, you’re neither setting the desired accuracy nor the delegate. Delete the locationManager = CLLocationManager() lines and you should have better results.
As ohr pointed out, an additional permission was required. The following line was added to the lazy locationManager initializer to rectify this omission:
manager.allowsBackgroundLocationUpdates = true
In accordance with Noah and Paulw11's advice, the getPermission function was deleted, and the line:
locationManager = CLLocationManager()
was removed from viewDidLoad, as there was a great deal of redundancy present. This alone did not fix the problems, but adding logic to the locationManager func so that recent map data was stored to an array, like so:
let annotation = MKPointAnnotation()
annotation.coordinate = newLocation.coordinate
locations.append(annotation)
while locations.count > 100 {
let annotationToRemove = locations.first!
locations.remove(at: 0)
}
resulted in working code. This solution was derived from code found here: https://www.raywenderlich.com/92428/background-modes-ios-swift-tutorial
(Thanks, Ray)
Ultimately the code functioned in the foreground and background without removing anything from the class or implementing anything in appdelegate.

Get user current location(lat/long) in all app state not always only when My app required without location manager delegate

I am working on watch heart beat app and I want when user heart rate critical then we will get his current location in(foreground, background and terminate) and send it to our server. Is there any way through which I get user location only at that position. I don't want to update his location every time.
To get user location you have to declare :
let locationManager = CLLocationManager()
in your controller.
Then, in viewDidLoad you have to request for location and initialize the CLLocationManager get process :
// Ask for Authorisation from the User.
self.locationManager.requestAlwaysAuthorization()
// For use in foreground
self.locationManager.requestWhenInUseAuthorization()
if CLLocationManager.locationServicesEnabled() {
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters
locationManager.startUpdatingLocation()
}
You will get location in CLLocationManagerDelegate :
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
var location:CLLocationCoordinate2D = manager.location.coordinate
print("locations = \(location.latitude) \(location.longitude)")
}
In your info.plist you have to add NSLocationAlwaysUsageDescription and custom alert message to show while requesting for location.
cheers...

Resources