I am working on a project in which i am trying to get location updates in all states, even when app is terminated. I have used all possible solutions but still it's not working in case of termination.For now I want to clear a doubt - I am using startUpdatingLocation() for foreground and background. As we know that startMonitoringSignificantLocationChanges() is the only method that relaunch app in case of any location update. Can we call "startMonitoringSignificantLocationChanges()" in applicationWillTerminate() method? and Will that work and relaunch app when there is any significant location update? Please tell me.
Thank!!
You cannot do that in applicationWillTerminate(),because closure won't return a value right now.If you want to get user location all the time,try Background Mode.
This is the description in Apple Document:
In such a case, the options dictionary passed to the application:willFinishLaunchingWithOptions: and application:didFinishLaunchingWithOptions: methods of your app delegate contains the key UIApplicationLaunchOptionsLocationKey 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.
It clearly tells you how to get the location.
Related
I'm working on a navigation application, everything working in terminated, background and fore ground state.
But in one scenario of terminated state startMonitoringSignificantLocationChanges is not handling itself.
The issue is
when i start the startMonitoringSignificantLocationChanges and killed the app, then I'm getting location event like after 0.5-1km because of that it draws straight line from my initial position to the first location event I get.But when the location event starts coming then everything work smoothly
Same issue occur again when in the middle of travelling I open the application to check my route status and then kill the application, again location events start coming after 0.5-1km and a straight line was drawn.
The code is straight
significantLocationManager = CLLocationManager()
significantLocationManager?.allowsBackgroundLocationUpdates = true
significantLocationManager?.pausesLocationUpdatesAutomatically = false
significantLocationManager?.requestAlwaysAuthorization()
and call the tracking when user needs by
significantLocationManager?.startMonitoringSignificantLocationChanges()
Rest I have handled the incoming location event in the app delegate to save in db.
So question is how should I handle this scenario in which straight line is drawn ?
From Apple documentation:
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.
If you need to receive location updates as soon as possible I'd recommend to use startUpdatingLocation() with desired distanceFilter of CLLocationManager.
You can use Location update in background mode. From Apple documentation:
When you start the significant-change location service, a recently
cached value may be reported to your delegate immediately. As new
location data is obtained, the location manager calls your delegate's
locationManager(_:didUpdateLocations:) method with the updated values.
The locations parameter always contains at least one location and may
contain more than one. Locations are always reported in the order in
which they were determined, so the most recent location is always the
last item in the array, as shown in Listing 2.
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let lastLocation = locations.last!
// Do something with the location.
}
Here you will get the last cached location in your device, and it should be very precise if you have location service turned on in your device of course.
Another thing to know is this. Note form Apple:
The significant-change location service requires authorization. For
more information Requesting Authorization for Location Services.
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.
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
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...
Location update even when app is killed/terminated
Please help.
It look like you don't save change in database.
Add log and check what happen.
Database file can be locked if device is locked with passcode. And it can happen with SignificantLocationChanges. I hope it link help you - IOS app unable to access files in background when screen locked with passcode
P.S. Also for startMonitoringSignificantLocationChanges() you don't need enable "Location updates", it need for startUpdatingLocation()
startMonitoringSignificantLocationChanges needs background location enabled?
UPD:
https://developer.apple.com/documentation/corelocation/cllocationmanager/1423531-startmonitoringsignificantlocati
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.
location should have in youre launchOptions, i recommend just print launchOption in console. And see it with (viewable from XCode -> Devices -> {your device} ), what keys are present?
I would like to clarify a few moments about traking user location while app is suspended. I have read a lot of articles about it but didn't find any clear answer.
Is it possible to create a local notification based on a user's location when application is suspended?
If it's possible, how my app's architecture has look like? Is my CLLocationManager subclass instance needs to be declared in AppDelegate file or it can be created as variable of some controller?
There are a couple of different ways to handle this.
You can set up a region-based local notification. That displays a message to the user if your app is not in the foreground. Your app only gets notified/launched if the user taps the action button on the local notification.
Another way to handle it:
You use the Core Location manager to create "geofence" regions that the system monitors on your app's behalf.
When your app is launched you should create an instance of the location manager and set up a delegate. You need to handle the process of asking the user for permission for location updates, and permission for always monitoring the user's location. That is a fussy, multi-step process, and if you miss a step it doesn't work. See the docs for more information. (I always have to back and re-read them when I'm setting up a new app with location services, and usually don't get it right the first time.)
When you've done that, the system will launch your app if it's not running when you receive a region enter/exit event. Once you create the location manager and set up a delegate, that delegate gets notified about the region enter/exit event.
In your handler for region enter/exit events you can post a local notification to yourself if you want to.
You can register the user for a local notifications using the region property.
//latitude & longitude come from your CLLocationManager delegates
let region = CLCircularRegion(center: CLLocationCoordinate2DMake(45.5017, 73.5673), radius: 1500, identifier: "identifier")
region.notifyOnExit = false
region.notifyOnEntry = true
let notification = UILocalNotification()
notification.region = region
notification.regionTriggersOnce = true //only show this notification once
notification.alertTitle = "Foo"
notification.alertBody = "Hello World"
UIApplication.shared.scheduleLocalNotification(notification)
Note that you can have at most 64 local notifications:
https://developer.apple.com/library/ios/documentation/iPhone/Reference/UILocalNotification_Class/
UPDATE: "You can only monitor a maximum of 20 location regions at one time. (and that's a combined total of 20 geofence regions and beacon regions.)" - DuncanC
Is it possible to create a local notification based on a user's location when application is suspended?
Yes. When you use background location monitoring, if your app is not active, it is woken in the background long enough to receive an event from the runtime. Thus, your app is now temporarily running. At that moment, creating a local notification is legal.
If it's possible, how my app's architecture has look like? Is my CLLocationManager subclass instance needs to be declared in AppDelegate file or it can be created as variable of some controller?
The event from the runtime is going to be sent to your location manager's delegate. Therefore your location manager needs to exist and it needs to have a delegate. It doesn't have to be a property of the app delegate, but it certainly needs to be a property of some instance that actually exists, so it if is a view controller, it had better not be a view controller that is not always present.
Note that if your app has been terminated while suspended (which is always a possibility), it will be launched from scratch (in the background) in order to receive this event. In that case, you can learn from the options: dictionary in didFinishLaunchingWithOptions: that this is because of an incoming location event, and thus you can respond by doing whatever is necessary in order to get yourself a location manager and a delegate.
I need to check for device location every one hour or so and if the location is outside a particular area ( say the device has been taken out of the office premises ), do some action (like show a local notification saying "Hey! The device is outside the office").
To do this, I need to keep checking for location every one hour even though the app is killed. How to keep the app stay alive like forever though it has been terminated by the user.
Yes you can do it but if your system is deployment target is greater then 7.0.
In ios7.1 there is method called startMonitoringSignificantLocationChanges. This method updates location in background and even if application is terminated as per apple document:
Starts the generation of updates based on significant location changes.
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 UIApplicationLaunchOptionsLocationKey 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.
I found one demo for this may this help you. look this link http://mobileoop.com/getting-location-updates-for-ios-7-and-8-when-the-app-is-killedterminatedsuspended
Thanks for your answers. The best way do this is to use Geofencing.
It uses startMonitoringForRegion:(CLRegion *)region where we provide the latitude and longitude of the centre of the region and also the radius of the region.
When the device moves into this region, didEnterRegion gets called.
When the device moves out of this region, didExitRegion gets called.
And yes it works even when the app is terminated/backgrounded.
In addition, we have to set NSLocationAlwaysUsageDescription in the App's Info.plist file which you will find under the Supporting Files.
View this article for detailed information.
http://www.devfright.com/how-to-use-the-clregion-class-for-geofencing/
Inorder to test this in a simulator, I used local notifications when it enters and exits the region using UILocalNotification in the didEnterRegion and didExitRegion methods.
`UILocalNotification *notification = [[UILocalNotification alloc] init];
notification.fireDate = [[NSDate date] dateByAddingTimeInterval:1];
notification.alertBody = #"You have entered the region";
[[UIApplication sharedApplication] scheduleLocalNotification:notification];`
Now change the custom location of the iOS simulator (Debug -> Location -> Custom Location), and provide latitude and longitude within the region and you get a notification saying "You have entered the region" and change the custom location to latitude and longitude outside the region, you will get a notification saying "You have exited the region".
I might be confusing how region monitoring works, but this is what i have so far:
I am registering a region to monitor through my location manager, which is implemented on a singleton class, this singleton is also set as the delegate of the location manager so the implemented method is being called.
- (void)locationManager:(CLLocationManager *)manager didEnterRegion:(CLRegion *)region
This works totally as expected, if the app is active or suspended the method is being called. It also makes total sense because the class has been already loaded and when the region enter event occurs iOS sends this even to my app which calls the location manager who registered (probably has a reference to it) and in turn it calls whatever delegate was also registered along it (since the class is there ready and loaded).
The issue is, what happens when the app has been killed? Is it first launched into the background? How does iOS know what delegate method to call, and if it has already been loaded?
When your app has been killed and gets started for a location update there can't be a location manager delegate yet and as such there are no notifications delivered to that delegate. The system can't know which of your classes should be used as a location manager delegate or how to instantiate it.
Instead your application:didFinishLaunchingWithOptions: gets called as usual, but the UIApplicationLaunchOptionsLocationKey is set in the options dictionary. That tells your app that you need to instantiate a location manager and set it's delegate. Only after you did this the delegate gets called with the region updates.