NSTimer in background mode using locationManager delegate ("Cheat way") SWIFT - swift

I have a function that I want to run in the app background mode and be executed at least every minute to check for wifi connection and to make decisions. I am using locationManager delegate so I can run my NSTimer in the background. However the location manager is consuming a lot of battery power. This app is not for apple release. However I am looking for more efficient settings for location manager so it will not be that power hungry or maybe any other good ideas?.
The current settings that I have are ok, but since I enabled automatic pause for location manager, function updates are delayed too much. Before I was using two delegates methods (didEnterRegion and didExitRegion) those were more power-hungry and not accurate. I read tons of available tutorials and checked other related posts on Stack overflow but have not found anything that would help me to solve my problm
Here is what I have in my delegate function:
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
self.timer = NSTimer.scheduledTimerWithTimeInterval(45, target: self, selector: #selector(self.checkNetworkSSID), userInfo: nil, repeats: true)
manager.stopMonitoringSignificantLocationChanges()
manager.stopUpdatingLocation()
}
Here is what I have in my viewDidLoad and AppDelegate
manager = CLLocationManager()
manager?.delegate = self
manager?.requestWhenInUseAuthorization()
manager?.startUpdatingLocation()
manager?.desiredAccuracy = kCLLocationAccuracyThreeKilometers
manager?.pausesLocationUpdatesAutomatically = true
manager?.activityType = CLActivityType.Fitness

For getting Location in Background you need to call this code on your
code and make sure add NSLocationAlwaysUsageDescription inside your info.plist
locationManager = CLLocationManager()
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyThreeKilometers
locationManager.pausesLocationUpdatesAutomatically = false
locationManager.startMonitoringSignificantLocationChanges()
if #available(iOS 9.0, *) {
locationManager.allowsBackgroundLocationUpdates = true
}
locationManager.requestAlwaysAuthorization()
locationManager.startUpdatingLocation()

Related

Why location service background not working if call LocationManager by Button_Action swift

I'm have doubt about background service of location if we call location manager in viewDidload we can manage call location when app will terminate by we know location running by push notification message
I'm set info.plist done , import all I think is use location and notificationcenter done.
override func viewDidLoad() {
super.viewDidLoad()
locationManager.allowsBackgroundLocationUpdates = true
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters
locationManager.startUpdatingLocation()
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
if(!gpsEnable) {
print("update disable")
return
}
print("update enable")
for location in locations {
processLocation(location)
}
}
But if call by Button it's not working when app will terminate
#IBAction func location(_ sender: Any) {
locationManager.allowsBackgroundLocationUpdates = true
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters
locationManager.startUpdatingLocation()
}
I expect to know that the reason to work cannot be called location same the working of ViewDidload.
I don't speak English very well thank you.
UPDATE
I know process why not working when not call in viewDidload if app will terminate
if you ever did project about keep data log of location when app will terminate then location it's will running location update when you move til location current of you change and call method of LocationUpdate or CLVisit or CLLocationChange and another you know that in your project.
ok .
First
You should call this in your somewhere view controller in project:
override func viewDidLoad() {
super.viewDidLoad()
locationManager.allowsBackgroundLocationUpdates = true
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters
locationManager.startUpdatingLocation()
}
if you not call this location manager(location update) it's will be not work when you kill app.
Regular when kill app we must move for location change position then in your project location begin work process locationUpdate or if you want to know now you can turn on again in privacy Location
Second
condition location update if app will terminate when function must be call use in ViewDidLoad in first ViewController only.
if you ask me why it's work first ViewController only.
I will answer "I don't know ,but we test for process in my app test til know process run specifically in first ViewController in ViewDidLoad, ViewWillAppear,ViewDidAppear ".
look in image this:
RootView ,SecondView
we test process by look in root view ,yes it's first view when I call location manager in view
override func viewDidLoad() {
super.viewDidLoad()
locationManager.allowsBackgroundLocationUpdates = true
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters
locationManager.startUpdatingLocation()
NotificationStatic.shared.showNotification(title:"viewDidload01",body:"did load",identifierRequest : "view1")
}
when process come in viewDidload process will can run process in location update when app will terminate
But you call location manager in SecondView but in RootView not call location it's will be work !!
if you ever perform to SecondView then in SecondView -> run ViewDidload (have location manager) then kill app then open location service again.it's work cause you ever call use method of location manager ,but it's can't get location log because in RootView(first view controller) haven't function about location update in viewDidload
conclude
in process of location service background it's will be work only in ViewDidload, ViewWillAppear, ViewDidAppear process in first view controller of app
I'm newbie skill for describe and newbie talk English thank you.

After requestAuthorization on first load the rest of viewDidLoad isn't executed and the next line jumps to the delegate functions (swift 3)

My app seems to work fine on each use except the first one.
I ask for user authorization and I have the appropriate keys in the plist but the rest of viewDidLoad after the lines requesting authorization don't execute. I have attached the breakpoints below and breakpoint 2 isn't hit on the first time the app is used.
I'm pretty sure after authorization is given it just jumps to the func locationManager in the extension.
I could wait till the very end to ask for authorization until everything else is set but not sure if this is the best, or only way, out.
Thanks,
class MapController: UIViewController, GMSMapViewDelegate {
var locationManager = CLLocationManager()
var currentLocation: CLLocation?
#IBOutlet var mapView: GMSMapView!
override func viewDidLoad(){
super.viewDidLoad()
locationManager = CLLocationManager()
--------------------------> breakpoint 1
locationManager.requestAlwaysAuthorization()
locationManager.requestWhenInUseAuthorization()
-------------------------> breakpoint 2
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.distanceFilter = 50
locationManager.startUpdatingLocation()
locationManager.delegate = self
guard let lat = locationManager.location?.coordinate.latitude else {return}
guard let lng = locationManager.location?.coordinate.longitude else {return}
mapView.settings.compassButton = true
mapView.settings.myLocationButton = true
mapView.isMyLocationEnabled = true
let camera = GMSCameraPosition.camera(withLatitude: lat, longitude: lng, zoom: 1)
mapView.camera = camera
mapView.delegate = self
getData()
}
extension MapController: CLLocationManagerDelegate {
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
guard let location: CLLocation = locations.last else {return}
let camera = GMSCameraPosition.camera(withLatitude: location.coordinate.latitude, longitude: location.coordinate.longitude, zoom: 1)
mapView.animate(to: camera)
}
}
Why do you request authorisation to two different things? If you request and get always authorisation, you don't need to request when in use authorisation, since this is only a subset of always authorisation.
Also, these are both asynchronous functions, so you cannot execute location based code right after them, since if you don't have authorisation yet, the code right after requestAuthorization() will be executed before you actually got authorisation and hence the functions won't be called, since you don't have authorisation yet.
You have to check the authorisation status before calling any location related code, such as locationManager.startUpdatingLocation() and only execute the location related code if the status is authorised. If it is not authorised, you have to implement CLLocationManagerDelegate's locationManager(_:didChangeAuthorization:) function and call the location related call inside that function after checking that the result of the change is an authorised status.

swift - run app in background continuously

I have been reading apple developer sources on how location updates work and reading tons stack overflow questions on it as well. Im trying to get my users location on my app all the time when the user put the app in the background. The code below is not my code, I found it from another stacker overflow source question and it is the only thing that gets me close to what i need. the code below works and updates me on the users location for 25 minutes only and then just stops, but what i want is it to work all the time as long as the app is in the background :(. The question was answered but I'm not quite understanding how this whole background stuff works. I've heard apple automatically terminates your app after 3 minutes or so, so anyway to bypass that would help. And also i do have my background modes checked as in Location updates and background fetch and even the info.plist that are needed as well which was said was suppose to automatically wake my app once terminated but that doesn't seem to work as well either. If anyone can help me this would be a tremendous help for my new app, it is the last bit of code i need aha, thank you in advance everyone. would also like to do it without adding a silent audio file so my app doesn't get rejected :)
override func viewDidLoad() {
super.viewDidLoad()
self.locationManager.requestAlwaysAuthorization()
self.locationManager.requestWhenInUseAuthorization()
self.locationManager.allowsBackgroundLocationUpdates = true
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.startUpdatingLocation()
var timer = NSTimer.scheduledTimerWithTimeInterval(5, target: self, selector: "updateLocation", userInfo: nil, repeats: true)
}
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]){
if UIApplication.sharedApplication().applicationState == .Active {
} else {
backgroundTaskIdentifier = UIApplication.sharedApplication().beginBackgroundTaskWithExpirationHandler({ () -> Void in
self.backgroundTimer.invalidate()
self.backgroundTimer = NSTimer.scheduledTimerWithTimeInterval( 60.0, target: self, selector: "updateLocation", userInfo: nil, repeats: true)
})
}
}
func updateLocation() {
var timeRemaining = UIApplication.sharedApplication().backgroundTimeRemaining
print(timeRemaining)
if timeRemaining > 60.0 {
print("timeRemaining > 60.0")
}
} else {
if timeRemaining == 0 {
print("timeRemaining = 0") UIApplication.sharedApplication().endBackgroundTask(backgroundTaskIdentifier)
}
backgroundTaskIdentifier2 = UIApplication.sharedApplication().beginBackgroundTaskWithExpirationHandler({ () -> Void in
self.backgroundTimer.invalidate()
self.backgroundTimer = NSTimer.scheduledTimerWithTimeInterval( 60.0, target: self, selector: "updateLocation", userInfo: nil, repeats: true)
})
}
}
You cannot (at present) run an app in the background continuously. That's how Apple intends things to work and you'd have to accept that. Once your app is in the background, it can be terminated at any point in time by the core OS.

CLLocationManager is slow getting location, Swift

Im creating this app, and it needs to get the users location - its all working properly, the thing is, that the time from accepting the use of location services, to getting the actual location takes like 5 seconds - is this normal?
I've used other apps, where it goes much faster..
Here's what my code looks like:
override func viewDidLoad() {
super.viewDidLoad()
// Ask for Location-Authorisation from the User.
self.locationManager.requestWhenInUseAuthorization()
if CLLocationManager.locationServicesEnabled() {
locationManager.delegate = self
locationManager.requestLocation()
}
mapView.delegate = self
}
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let locValue: CLLocationCoordinate2D = manager.location!.coordinate
let initialLocation = CLLocation(latitude: locValue.latitude, longitude: locValue.longitude)
self.centerMapOnLocation(initialLocation)
}
func locationManager(manager: CLLocationManager, didFailWithError error: NSError) {
print("could not get location")
}
But the time from the application gets the location to put into the centerMapOnLocation-function, seems just to be quite long. What is to be expected, when getting a users location? I'm testing on a wifi connection, so I know its not because the internet is slow, or its a bad connection...
Anyone have an idea? :)
Best regards!
Try setting the accuracy and use locationManager.startUpdatingLocation(). I do that, and get answer within a second (on the device).
From the documentation of requestLocation():
This method returns immediately. Calling it causes the location manager to obtain a location fix (which may take several seconds) and call the delegate’s locationManager(_:didUpdateLocations:) method with the result.
Source
So basically, everything is fine with your code, it's just how the framework is built.
When initializing your location manager, add startUpdatingLocation():
let manager = CLLocationManager()
manager.delegate = self
manager.requestWhenInUseAuthorization()
manager.requestLocation()
manager.startUpdatingLocation()
Without startUpdatingLocation() geo-location takes about 5 seconds, with it, the request executes nearly immediately.
If you don't want to delay the app's launch for the location manager, consider deploying two location managers (in the app delegate), tasking one with generating a location quickly and the other with generating a location accurately:
fastLoc.delegate = self
fastLoc.desiredAccuracy = kCLLocationAccuracyThreeKilometers
fastLoc.requestWhenInUseAuthorization()
fastLoc.startUpdatingLocation()
bestLoc.delegate = self
bestLoc.desiredAccuracy = kCLLocationAccuracyBest
bestLoc.requestWhenInUseAuthorization()
bestLoc.requestLocation()
The combination of 3 km accuracy with startUpdatingLocation() should return a location almost instantly, almost always before the root view controller is even ready to go. bestLoc manager is likely to return a location well after the user has launched the app but it will be very accurate.
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
switch manager {
case fastLoc:
fastLoc.stopUpdatingLocation()
deviceLocation = locations.last! // set property with fast loc
case bestLoc:
deviceLocation = locations.last! // overwrite property with best loc
default:
break
}
}

requestLocation() in iOS 9 calls the didUpdateLocation delegate multiple times

So there is the new requestLocation() method on iOS9. I am trying to use it to get only one location update but it hits the didUpdateLocations multiple times.
Isn't this suppose to call it only once ? i have set distanceFilter to 1000.0, so it is pretty broad to help return quickly. Any ideas ?
even if i call the stopUpdatingLocation() inside the delegate method, i still get three hits of the delegate.
Note: same behavior occurs when i use StartUpdatingLocation instead, i want only a single return as i want to obtain the user's current country, so feed the location to reversegeocoder
thanks in advance
Here is the code:
func getLocationOneTime(accuracy: Double){
print("Acquiring location one time - accuracy requested:\(accuracy)")
locationManager.distanceFilter = accuracy
locationManager.desiredAccuracy = accuracy
// Start gpsOneTimeTimeout timer
gpsTimeoutTimer = NSTimer.scheduledTimerWithTimeInterval(gpsOneTimeTimeout, target: self, selector: #selector(self.reportTimeout), userInfo: nil, repeats: false)
locationManager.requestLocation()
}
Usually for this kind of problem I just use an if statement to prevent the delegate function changing anything after the first call
in this case if you are just looking for the location I would do:
let locationManager = CLLocationManager()
var userLocation: CLLocation!
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
if userLocation == nil {
if let location = locations.first {
userLocation = location
self.locationManager.stopUpdatingLocation()
}
}
}
Edit: I had a look at the apple documentation for this and you are doing things right, I doubt you'll be able to prevent it through any reasonable means sadly.