I am working on an app that records journeys by tracking the GPS. Everything is working fine in background if we start the process from the foreground (by tapping the button "Start Journey").
Now the idea is start record these journeys automatically triggered by a iBeacon. When the iPhone gets inside of a beacon region, the app is detecting this and calls the function LocationManager.StartUpdatingLocation();
PROBLEM:
Using iBeacons from background, we only get 10 seconds ranging and that is the same figure I have got to get location updates from GPS.
All I need is detect I am inside of beacon region, start GPS and keep it running, and only disable the GPS when I am outside of the region.
Unfortunately, you can use CoreLocation geofences in the background, but you can't get fine GPS updates continually. This isn't a Xamarin thing -- it is an iOS restriction.
I wrote the blog post that #RobertN referenced about extending background beacon ranging to 3 minutes. But I don't think this is much help to you because you want to do get GPS updates continually, which Apple simply does not allow.
We actually recently wanted to implement this behavior in one of our apps too, and found that if we start significant location updates (startMonitoringSignificantLocationChanges) in the foreground, then we're able to start regular location updates (startUpdatingLocation) in the background, in our didEnterRegion implementation.
(Naturally, your app needs the "always" authorization to access Location Services, and the "Location updates" Background Mode enabled.)
Our app is still pending review, so whether that's a bug or a feature of Core Location, and whether Apple is okay with that, remains to be seen.
The best explanation to this I seen can extend that 10 seconds to 3 minutes, but that is it... App rejection is an issue with continuous background operation unless your app truly is reviewed as a Navigation app:
A second approach involves tracking the beacon in the background,
noting its estimated distance, and only triggering an action when the
beacon is estimated to be within a specific range. This approach is
problematic on iOS, because CoreLocation generally allows only 10
seconds of ranging time when an app is in the background. If a beacon
is first detected at 50 meters, and a person is approaching the beacon
at one meter per second, the mobile device will still be 40 meters
away when iOS suspends the app and stops it from ranging.
The good news is that it is possible to extend background ranging time
on iOS. If your app is a navigation app, you can specify location
updates in the “Required background modes” in your Info.plist. But
this approach makes it harder to get AppStore approval -- you have to
convince reviewers that your app is providing navigation services to
the user. This probably isn’t true for many apps that simply want to
use beacons to trigger at a specific distance.
Fortunately, you can still extend background ranging time without
requesting special background modes. The time you can get is limited
-- only three minutes. But this clock restarts each time your app is woken up in the background, meaning you can get an extra three minutes
of ranging time each time your app detects a beacon (enters a beacon
region) or stops seeing beacons (exits a beacon region.)
Via: http://developer.radiusnetworks.com/2014/11/13/extending-background-ranging-on-ios.html
Related
I make iBeacon-based application. I want to reach better proximity than gives major-minor, I want to use ranging. Problem, that it doesn't work more then 10 seconds in background. Ok, I use startUpdatingLocation and ranging works fine ... until device is rebooted. After reboot, ranging doesn't work. I think it because of startUpdatingLocation doesn't perform in background and after rebooting application doesn't become foreground. Have anybody solve the same problem, how to start ranging after rebooting device?
Only monitoring can launch an app into the background based on a beacon detection. To do what you want, you have to both monitor for beacons and range for beacons at the same time. The monitoring didEnterRegion callback will trigger a background launch of your app, and then ranging will start automatically if you have it set up.
locationManager.startMontoringForRegion(beaconRegion)
locationManager.startRangingBeaconsInRegion(beaconRegion)
You can also extend ranging beyond 10 seconds for up to 3 minutes, as described in my blog post here.
I am developing an iOS app to monitor iBeacons. It works well in both foreground and background. I also need it to keep monitoring even when the screen is off. Now my problem is, when I turn off the screen with the shoulder button, NSLog shows that the iBeacon signal goes off (RSSI=0, beacon.accuracy=-1.0) accordingly, and 10 seconds later, there is no beacon found at all, while the delegate method locationManager:didRangeBeacons:inRegion: is called continuously. It seems that the app is running when the screen is off but iOS turns off the location service for iBeacon ranging. I tried to set "locationManager.pausesLocationUpdatesAutomatically = NO;" but still doesn't work. Is there any advice on this issue? Thanks in advance.
It seems that you're doing ranging instead of monitoring. Ranging only works when the app is active—either in the foreground or in the background. The catch is, iOS will automatically put an app to sleep a few seconds after you navigate away from it, or lock the screen.
What you have to keep in mind is, there's really no such thing as "permanent background state" on iOS. Apps run in the background for some limited time while transitioning to the suspended state (to let them, e.g., finish uploads/downloads), and there are also Background Modes that sometimes enable an app to run in the background, but these are usually reserved for very specific apps—e.g., the "audio" Background Mode means an app can be running in the background as long as it plays music, etc. There is a Background Mode for "location" services, but it's reserved for turn-by-turn navigation apps.
Beacon monitoring however continues running even if your app goes to sleep, or gets terminated by the iOS due to memory pressure. When you go in or out of range of a beacon, iOS will launch your app into the background to handle the event, giving it a few seconds (which you can extend up to a few minutes with a Background Execution Task) to do so (you can range during that time), but then it's back to sleep.
You might also want to take a look at this question:
Receive signal from beacon while app is in the background
Still fighting with iBeacon monitoring for screen-off mode in my iOS app.
In my experiment, when the screen is turned off, the delegate method "locationManager:didRangeBeacons:inRegion:" is still triggered continuously, but, as soon as the screen is off, the signal disappears accordingly(RSSI=0, beacon.accuracy=-1.0), and then, 10 seconds later, there is no beacon found at all.
I found some important information in this post saying that "iOS uses beacons in two different ways: region monitoring and beacon ranging. The latter does NOT work in the background ..., or when the app is terminated.
CLLocationManager will ONLY fire off ONE delegate call when a region is entered. If you start monitoring for a region while inside of that said region, the entry delegate will NOT be called. Your app will have to manually call the CLLocationManager’s requestStateForRegion method. Once you’ve exited the monitored region(s), CLLocationManager will call the didExitRegion approximately 30-45 seconds later."
Now I tried to call the "requestStateForRegion:" method continuously when the screen is off. The result is:
If my iPhone is already in the iBeacon region, the delegate method
"didDetermineState" gives "CLRegionStateInside" continuously;
When I hold my iPhone and walk outside the iBeacon region, or just
shut down the iBeacon's advertising, after 30-45 seconds, the
delegate method "didDetermineState" turns to "CLRegionStateOutside"
and keeps in "outside" state;
When I hold my iPhone and walk inside the iBeacon region, or turn on
the iBeacon's advertising again, the delegate method
"didDetermineState" still gives "outside" continuously and NEVER
TURNS BACK TO "inside".
What I need is, when the screen is off, I can detect as soon as my iOS device enters the iBeacon region. Asking for help...Thanks in advance.
Have you added the NSLocationAlwaysUsageDescription in your info.plist? I can really recommend you to follow this tutorial which goes through all this.
When you start monitoring, there's always an initial call to the didDetermineState delegate. This is how you can figure out if you're already in the beacon region. From then on, you can continue relying on didEnter/didExit (didDetermineState is also called alongside these). The only catch is, if the user turns Bluetooth off and then back on again—you will lose any state transitions that ordinarily would've happened during that time—so it's a good idea to call requestState after you detect Bluetooth is back on, to get caught up on the current state.
All in all, there's no need to call requestState continuously, and you can't ordinarily even do so when the screen is locked—iOS will put your app to sleep, per my answer to your other question. Unless you're using Background Modes to keep the app running in the background, but then you must be able to defend it when you submit your app for review, as Background Modes are reserved for very specific use cases. You should rely on automatic calls to didEnter/didExit/didDetermineState, aided by requestState if Bluetooth is turned off and on.
When I hold my iPhone and walk inside the iBeacon region, or turn on the iBeacon's advertising again, the delegate method "didDetermineState" still gives "outside" continuously and NEVER TURNS BACK TO "inside".
Keep in mind that depending on hardware capabilities of your iOS device (you haven't mentioned which one you're testing with), the "enter" event might take a while to trigger. The guys at Radius did some testing long time ago, and it was up to 15 minutes for iPhone 4S on iOS 7.1. That's b/c iPhone 4S doesn't support offloading BLE scanning to the Bluetooth chip, unlike newer iPhone models. But even on these newer models, there's a limit of how many scans can be offloaded to the chip, which Radius also measured to be 30 (on iOS 8.3 and with 3 different iOS devices).
With the M7 chip in the latest iOS devices one can get programmatically notified as the user goes from stationary to running, walking, etc using CMMotionActivityManager. Stava and Runkeeper have both used this to auto-pause GPS polling (shut off the GPS antenna) when it detects the user isn't moving via the M7, and then re-enable GPS updates once they are moving again. It is able to do this while the app is in the background state, which is the key here.
The issue I run into while duplicating this functionality is that if I turn off GPS updates while my app is in the background I stop receiving activity updates, and can no longer detect when the user moves again via the M7 to turn the GPS back on.
If I leave the GPS running the whole time I'll continue to get movement updates from Core Motion the entire time the app is in the background.
I'm assuming they aren't playing white-noise or some other cheap trick to stay active. How did they go about this?
RunKeeper actually does use the audio trick to stay awake. If you open up the app package and check their Info.plist you will see that it registers for the background audio mode. This is how they pull off periodic audio notifications of your distance, speed, and pace. It is also how they stay awake during your run while minimizing battery drain.
If you noticed that the Location Services icon (the triangle in the status bar) disappears completely while using RunKeeper then they definitely are not using any type of location tracking to accomplish background execution. Even activating geo-fences and significant location change monitoring would cause the Location Services icon to appear.
They also aren't using the M7 to stay awake because it doesn't work that way. An update from the M7-related CoreMotion APIs will not wake up your app from sleep. When they app does wake up they would be able to query the Motion Activity and Step history and maybe try to compute something, but I doubt it would be all that accurate.
Finally you should note that the Auto-pause APIs were introduced in iOS 6 before the release of the iPhone 5s and M7 chip. They are orthogonal concepts.
have you considered experimenting with
application:performFetchWithCompletionHandler:
in the app delegate? You can't control how often it is called, but depending on the app, it can be every ~15 minutes. You can then launch the CMMotionActivityManager from there to query M7 results.
It's not entirely clear what functionality you are trying to replicate, but the M7 chip records all of the activity, regardless of whether your app is running. So you can simply query in the background and update step totals or activity type totals.
What I noticed when you turn off GPS, app will not execute any code in background for iOS 7, app looks like in inactive state. So better while moving to background use startMonitoringSignificantLocationChanges and also get updates from your location manager. Means simultenoulsy use both service startUpdatingLocation on user state change and startMonitoringSignificantLocationChanges in Background.
So when user Turn on GPS, while you used startMonitoringSignificantLocationChanges your app will receive
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
Better check here in background itself what wrong with CoreMotion Framework. And try to restart it.
Because wihtout M7 chip device I am able to read Accelerometer reading in such case.
If your location manager is working in Active mode, to enable background mode you need to do this three steps:
Check that [Target / Capabilities / Background Modes / Location updates] is enabled.
[locationManager requestAlwaysAuthorization];
locationManager.allowsBackgroundLocationUpdates = YES;
First, check if you have set up the background behave of your app.
Go to target - capabilities section and check Background mode for location updates.
So I asked a question about my code relating to didEnterRegion but perhaps I was being too specific, therefore could I ask someone to clarify the order of method calls in more generic terms for region monitoring, specifically when the app is in the background.
My understanding is:
App registers region calling startMonitoringForRegion:
User taps home button or locks device, app goes into the background.
The devices location is monitored at the OS level, separate from the app, the app is never launched by the OS to confirm the users current location.
When the user crosses the boundary into the region, the OS looks for which app originally registered the region and launches that app.
The app is launched in the background, (didFinishLaunchingWithOptions: is not called however), the CLLocationManager delegate is setup and it’s didEnterRegion delegate method is called.
In my case, this sets up a UILocalNotification which is presented immediately (banner displays on home screen if for example another app is in use, or on the lock screen if phone is dormant).
The user actions the notification by swiping in the lock screen or tapping the banner, the app is launched and appWillEnterForeground/appWillBecomeActive is called AND the app delegate didRecieveLocalNotification: method is called if implemented.
This is my understanding, which is probably wrong as my UILocalNotification is never fired if the app is in the background. Could someone clarify which bits are wrong?
After further testing I've come to the conclusion that there's nothing wrong with my code and it's actually appears to be Apples implementation of region monitoring being poor. It seems to be only slightly better than the monitoring of significant location changes, and still relies on changes in wifi networks and cell towers. Even within a major UK city I found didEnterRegion wasn't trigged until you were up to 1000 meters into a mile wide region, if triggered at all. This explains why it works every time when testing though Xcode and forcing the location.
The only work around I've come up with so far is to calculate the distance remaining each time the users location is updated and to manually call the didEnterRegion delegate method when this is less than the regions radius/2. This is supposed to be done automatically by Apples code when you're more than 10m into the boundary, however I found that to be unreliable for the reasons above.
However given you can't get regular updates using the better accuracy of GPS whilst the app is in the background and this is only a problem when the app is in the background, it's not really a solution at all. :o(