How to Monitor Location in Background in iOS? - objective-c

I am currently writing a home automation app for a client which will open their garage, turn on the living room and office lights, and other such things when they arrive within 150 meters of their home. It will do the opposite when they leave. I am very grateful that geofencing is built into iOS, and the feature works perfectly when the app is in the foreground, but when the user closes the app, and the phone is still monitoring a geofence, the app does nothing when an event occurs. Is there a separate method other than locationManager:didEnterRegion: that I should be implementing for background notifications to my app? Here is the code that I am using right now:
- (void)locationManager:(CLLocationManager *)manager didEnterRegion:(CLRegion *)region {
NSLog(#"Entered Region - %#", region.identifier);
[self showRegionAlert:#"Entering Region" forRegion:region.identifier];
[self sendCommand:true];
}
- (void)locationManager:(CLLocationManager *)manager didExitRegion:(CLRegion *)region {
NSLog(#"Exited Region - %#", region.identifier);
[self showRegionAlert:#"Exiting Region" forRegion:region.identifier];
[self sendCommand:false];
}

To use location services while your app is in the background you need to change your app's info plist file. Should be found in Supporting Files folder and look like this, YourAppName-Info.plist
Add an item to the Information Property List and type in Required Background Modes as the key, which is of type Array. Add an item to that array and type in App registers for location updates into the value of that item.

I discovered it was a non-issue. It all works exactly the same way as if the app were active. The same method is called. I made sure to optimize the app for background launch though so that it would be more of an instant response. I guess I was doing something wrong though, or it just wasn't working at first.

Turn on BackgroundModes in Target Settings/Capabilities and enable Location Updates. Since app is in background, you will get little time for processing and the app will move to sleep mode. Use expirationHandler make app active for some more time for doing large processing.

Related

background location iOS 8

Hello Devs: I am working on an app where I would like to fetch users location in background and send push notifications to him as soon as the user arrives at that particular location. Here is what I have done so far with my locatioManager in my app delegate
locationManager = [[CLLocationManager alloc] init];
[locationManager setDelegate:self];
[locationManager setDesiredAccuracy:kCLLocationAccuracyBest];
if (IS_OS_8_OR_LATER) [locationManager requestAlwaysAuthorization];
[locationManager startUpdatingLocation];
I have set up my info.plist to always request location. I also get the message that my app will be using location in the background when I install the app on my device. However when I close the app and arrive at the specific location I don't get any push notifications or alerts until I launch the app. I turned on Background mode --> location updates under capabilities section and then everything works absolutely fine. I receive notification seamlessly without launching the app. This is all good but when I close the app I see a blue bar on my status bar saying that my app is tracking the location in background. How do I hide that blue bar on the top? I am pretty sure this is going to scare away my users and they will remove my app instantly. To make long story short how do I accomplish this? I know this question has been asked and answered several times in past but all those answers are 2-3 years old and don't seem to work with new iOS 8. I need to get the user location in background in order for my app to work or else its useless. I will really appreciate any help or suggestions to this.
Thanks!
What you want to do is called (background) Geofencing. Your app doesn't need to calculate it by itself since CoreLocation already offers this feature.
Please have a look at this answer from Daniel.
The Geofencing feature will wake up your app when the users gets into the target zone, and will not display the blue bar.

When CLLocationManagerDelegate methods are called?

I am using iOS8 and trying to track location(latitude and longitude) values. I needed clarification about when CLLocationManagerDelegate methods are called.
1)Will it be called only when app is in foreground along with locationchange OR
2)Will it be called when app is in background along with locationchange OR
3)In both the above conditions.
I just wanted to know/clarification about whether - (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations is called even when app is in background along with locationchange OR only when app is in foreground along with locationchange OR in both conditions?
I have not enabled Location Updates Background Modes.I am using iOS8 and made necessary changes for location updates following the link.Using NSLocationAlwaysUsageDescription in my Info.plist.
Assuming that you asked the user for location permission (you have NSLocationAlwaysUsageDescription or NSLocationWhenInUseDescription keys in your info.plist) then didUpdateLocations is:
Always called when the app is in foreground mode
NOT called when the application is in background unless you enabled the background location mode.

Are there iOS devices that can monitor iBeacons but not range them?

Or asked another way will
[CLLocationManager isMonitoringAvailableForClass:[CLBeaconRegion class]]
and
[CLLocationManager isRangingAvailable]
ever return different values?
Short answer: No, there are not any iOS devices that can monitor iBeacons but not range them. Both methods will return the same value if isMonitoringAvailableForClass is given a CLBeaconRegion instance.
The reason the API looks this way is because the isMonitoringAvailableForClass method can be called with classes other than a CLBeaconRegion class. CLCircularRegion is used for monitoring geofence regions. The method might return NO when passed CLBeaconRegion on a device without LE Bluetooth, and return YES when passed a CLCircularRegion on the same device.
I believe there is one case when [CLLocationManager isMonitoringAvailableForClass:[CLBeaconRegion class]] will return NO and [CLLocationManager isRangingAvailable] will return YES.
If Background App Refresh is turned off [CLLocationManager isMonitoringAvailableForClass:[CLBeaconRegion class]] should return NO.
In Apple's Location and Maps Programming Guide under the "Determining the Availability of Region Monitoring" section:
Before attempting to monitor any regions, your app should check
whether region monitoring is supported on the current device. Here are
some reasons why region monitoring might not be available:
The device doesn’t have the necessary hardware to support region monitoring.
The user denied the app the authorization to use region monitoring.
The user disabled location services in the Settings app.
The user disabled Background App Refresh in the Settings app, either for the device or for your app.
The device is in Airplane mode and can’t power up the necessary hardware.
(I've bolded the fourth bullet point, because it's the case I'm talking about.)
Ranging is, however, only a foreground activity so Background App Refresh settings aren't important.
In this one case region monitoring will not be available, but ranging will be available.
Note: Currently, when Background App Refresh is turned [[CLLocationManager isMonitoringAvailableForClass:[CLBeaconRegion class]] returns YES, but when you start monitoring you'll never get notification and if you call requestStateForRegion: then locationManager:(CLLocationManager *)manager monitoringDidFailForRegion:(CLRegion *)region withError:(NSError *)error will be called on the CLLocationManager's delegate.
The error message will be "The operation couldn't be completed." with an error code of 4, which translates via CLError.h to kCLErrorRegionMonitoringDenied.
I hope Apple will fix the false positive in [[CLLocationManager isMonitoringAvailableForClass:[CLBeaconRegion class]] in one the next few updates.

Im trying to make an iphone app similar to find my friends how do i find the location of my friends on the map?

Im trying to make an iphone app like google latitude or find my friends.
I'm able to find my current location but how do i locate my friends?
And also how do i show that location on the map?
You can refer here to know about getting user current location.
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation {
NSLog(#"OldLocation %f %f", oldLocation.coordinate.latitude, oldLocation.coordinate.longitude);
NSLog(#"NewLocation %f %f", newLocation.coordinate.latitude, newLocation.coordinate.longitude);
}
You Must need a web service that stores locations of each devices when the app is running, then from your app send a query to the web-service from there you will find the list of device using the app.
It's not a very elegant solution since the user is always polling the server and the web request can fail more often than you'd want. I heard about socket programming where the user keeps a live connection to the server and when ever the server changes, the user is notified, sounds more like the right approach but also sounds much harder :P
see my answer here For Showing Multiple annotation on MAP.

Background task doesn't seem to get cancelled/ended

We're currently developing an iOS app that needs to check location in the background. At first, we tried to use significant location changes, but they aren't accurate enough/don't trigger often enough. We considered using region monitoring, but from what I've read online, that isn't always accurate either, and you also have the problem of a limited number of regions to monitor. (We may eventually try region monitoring.) At the moment, however, we're attempting to use the standard location updates to track the user location in the background, with a plan to have it at check at intervals of 5 minutes, or so.
The app is registered for location updates in the background (using 'App registers for location updates' for 'Required background modes'), and we start a background task which checks the location once, stops location updates, then uses NSThread sleepForTimeInterval: to (at the moment, while we're in development) pause the task for 10 seconds. It then checks the location once again, stops location updates, pauses for 10 seconds, etc.
This appears to work as expected... When the app goes into the background, we receive a log/notification with our location update every 10 seconds, and when the app is reopened, the logs/notifications stop. However, the problem is that when the app then goes into the background for a second time, it appears the original background task was never cancelled, and a new one is created, so there are now two tasks running, each checking location at 10 sec on intervals. If the app is opened/sent to the background multiple times, then a background task is started for each of them.
I thought about setting a flag to say "has the app been sent to the background at least once?", and only run the task if it's the first time it's sent to the background, but this seems to cause additional problems, and (as a relatively new iOS developer) I'm curious as to why the background tasks aren't being cancelled when the app enters the foreground.
The AppDelegate.h file contains...
#interface AppDelegate : UIResponder <UIApplicationDelegate, CLLocationManagerDelegate> {
UIWindow *window;
UINavigationController *navigationController;
UIBackgroundTaskIdentifier bgTask;
BOOL inBackground;
}
The AppDelegate.m file contains...
- (void)applicationDidEnterBackground:(UIApplication *)application {
inBackground = YES;
bgTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
[[UIApplication sharedApplication] endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
}];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
while (inBackground == YES) {
NSLog(#"%#", #"Check location...");
[locationManager startUpdatingLocation];
[NSThread sleepForTimeInterval:10];
}
[[UIApplication sharedApplication] endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
});
}
- (void)applicationWillEnterForeground:(UIApplication *)application
{
// 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.
inBackground = NO;
[[UIApplication sharedApplication] endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
}
The location updates are working as expected, I just can't work out why the background tasks aren't being cancelled/ended when the app enters the foreground. I do wonder if it's anything to do with the NSThread sleepForTimeInterval:, but I'm not sure if it is, or how to fix it (if indeed, it is). Thanks, in advance, for any help!
You don't manage location updates by sleeping and then requesting them. You manage location updates by setting "location" in UIBackgroundMode (as you do), and then implementing a CLLocationManagerDelegate. This has nothing to do with beginBackgroundTaskWithExpirationHandler:. That's for requesting additional time (up to about 10 minutes) to finish a given operation. You shouldn't be calling that at all just to get location updates.
Once you've registered as a location app in UIBackgroundMode, you will automatically get updates whenever the location changes within the accuracy you specified for your location manager. The system will do all the work for you.
What you're describing may actually hurt battery life because it frustrates the OS's ability to manage the multiple location sensors (of which the GPS is just one). Tell the OS what you need by setting the correct accuracy (if significant changes is too coarse), and let it do its job. Getting really accurate location from the GPS is expensive. You should do battery testing before assuming that it's cheaper to do every 5 minutes than to leave on. The best thing you can do to preserve power is to reduce the required accuracy. You might turn it down to a coarse level, and then when you come to the foreground move it to an accurate level. But keeping track of precisely where the user is every 5 minutes is going to be expensive. It's hard to fix that.
BTW, what you're really trying to do here is get to run "something" every 5 minutes. There is no mechanism for that in iOS. You can either ask for location services or not (and configure it in various ways). You can't ask for "I want to wake up every five minutes and ... do anything." After about 10 minutes you're going to be killed if you don't call endBackgroundTask:.
To your question of why the tasks aren't being cancelled, see How to use beginBackgroundTaskWithExpirationHandler for already running task in iOS. As I said, this "background tasks" is not the tool you want for this problem. It's completely unrelated.
I'm fairly certain that your instance variable bgTask is being reallocated when the app comes back into the foreground, so the value doesn't contain the identifier you're looking to kill. Consider saving this identifier in NSUserDefaults or something a little more permanent and retrieving it later.