I am currently using the Xamarin geolocation plugin found here:
https://github.com/jamesmontemagno/GeolocatorPlugin
To perform location services in an app I am building using Xamarin Forms (PCL).
I believe I have added in the relevant permission settings to allow for this.
The GPS works great while the app is active and locked (but with app in the foreground). However when the app is pushed to the background on iOS by clicking the "home" button, it still tracks the user and highlights the "App is Using Your Location" message as I would expect, however after a certain amount of time between 30-40 minutes, this message disappears, and the GPS appears to stop tracking the user until they bring the app back to the foreground.
Once the app has been brought to the foreground, it can be backgrounded once again for another 30-40 minutes to repeat the cycle.
I have ensured that the locator object allows background updates:
public static Plugin.Geolocator.Abstractions.IGeolocator locator;
locator = CrossGeolocator.Current;
locator.AllowsBackgroundUpdates = true;
locator.DesiredAccuracy = 20;
A call to .PausesLocationUpdatesAutomatically shows that this is false (which I believe is the default).
Edit
I have the following keys to info.plist:
NSLocationWhenInUseUsageDescription
NSLocationAlwaysUsageDescription
And enabled background location updates:
However I have not enabled background fetching as Girish has in the answers, is this something I need to do?
Please check whether you have enabled the background mode for location update.
Related
I have a navigation app that supports CarPlay connection.
When running with CarPlay, some users report that they appear to not have GPS connection.
Looking at the logs I noticed that on app launch we're getting didFailWithError with error code kCLErrorDenied, so according to Apple's recommendation we stop updating location.
This comes immediately after receiving locationManagerDidChangeAuthorization with kCLAuthorizationStatusAuthorizedWhenInUse with accuracy=0.
In this flow we also check CLLocationManager.locationServicesEnabled which returns true, CLLocationManager.authorizationStatus which returns kCLAuthorizationStatusAuthorizedWhenInUse and CLLocationManager.accuracyAuthorization which returns CLAccuracyAuthorizationFullAccuracy.
Users also confirm that they did give location permissions to the app and had location services enabled, also this issue was resolved after a few restarts of the app.
All the cases I encountered were on CarPlay using iOS 16.
Any idea why kCLErrorDenied could be triggered when there are location permissions?
Just in case someone else encounters this issue - this happens when we start location updates while the app is still in the background and we only have permissions for "While Using".
This is more prominent on CarPlay since it's possible to go to the home screen and disconnect. When reconnecting the app would wake up while in the background and try to start updating location.
The solution was to only start updating location when the app is in active state.
Some background
I am currently writing a UI Test for a settings pane, and click on buttons to enable certain permissions such as push notifications and location services.
However, if the alert for the permission has been displayed before (regardless of the user allowing or denying access to the permission), the alert will not display again, and will just take the user to the settings app. Unfortunately, these settings do not reset, meaning the first time I run the UI tests, alerts will show; and on all subsequent UI test runs, the buttons will take me to the settings app unless I reset the device before the tests begin.
My issue
Thus, my test needs to know if the app went into the background, and attempt to foreground it to continue the testing:
if app.state == background {
foregroundApp()
}
// continue with other tests
Is there any way to determine if the app is in the background?
What I tried
I researched methods to determine the state of the application (running/background/etc) from a UI test, and was not able to find much. I tried to check whether certain elements exist:
if (app.navigationBars.element.exists) ...
but this gives me runtime errors[1] if the user is taken to the settings page because the app under test is in the background, and the test cannot lookup the navigationBars (or other elements).
I tried using some of the methods from Facebook's private headers for XCUIApplication() and XCUIElement().
XCUIApplication().state always returns 3 no matter what state the app is currently in, and any attempts to call XCUIApplication().resolve() to foreground the app give me the same errors as before[1]
I tried to rewrite the logic to foreground the app before resuming the tests, but methods such as XCUIApplication().launch() kill the app before restarting, which I cannot do. Only siri service seems to work for me, but I cannot access the siri service through the corporate proxy, and modifying proxy permissions is not possible.
Is there any other way to check the app state?
Errors
[1] This error is printed every time I try to do something involving state. I do not call snapshotView anywhere, and thus the suggestion to use afterScreenUpdates is useless.
Failure to get snapshot within 15.0s
Cannot snapshot view (<UIKeyboardImpl: 0x7febcc75d000; frame = (0 0;
414 226); layer = <CALayer: 0x608000625720>>) with
afterScreenUpdates:NO, because the view is not in a window. Use
afterScreenUpdates:YES.`
tl;dr
I need to check whether the app I am UI testing has entered the background (i.e. user pressed the home button). Checking for existence of particular elements such as navigation bars doesn't work, neither do most methods from Facebook's private headers for XCUIApplication/XCUIElement. Foregrounding the app also causes issues, and relaunching the app is not an option; neither is siri service.
You can do this in Swift 4, using XCUIApplication.state, which will give you information about the state of the app - whether it's in the foreground or background etc. however, it's not possible to find this information in Swift 3 and below. Essentially, UI testing in Swift 3 doesn't support leaving the app.
Sorry if this question is too basic, but I'm stuck.
Scenario:
My app uses background and suspended locationUpdates using startMonitoringSignificantLocationChanges.
in my AppDelegate I have:
if let options = launchOptions {
locationKey = options[UIApplicationLaunchOptionsLocationKey] as! Bool
}
So when app is launched by location manager, my locationKey = true.
I was hoping to use locationKey inside the app to distinguish location manager launching or user launching.
The problem is:
When the app is launched by location manager, the behavior is exactly as when user launch the app, I mean, all views (from launch process) are instantiated, but app still "closed".
When I tap to open the app, it is "like" is already opened, so, I can't verify my locationKey, because it is always true and AppDelegate (didFinishLaunchingWithOptions) is not triggered again and then is quite hard to figure out how I can process the startup in its different ways, user and location.
My question is, how can I identify that the app was launched by location and when user really open the app I "restore" its normal way ?
Thank you and sorry if it's not clear
can you be a bit more clear about the behaviour you expect for your app?
The app won't ever be launched by location :
once the user launch the app (triggering didFinishLaunchingWithOptions),
the app can go into background mode (aka being suspended) and can do some work (like updating locations, finishing some tasks, or delegating unfinished downloads to the system so then can finish) or is killed.
When the user come back to the app either the app was in background and switch to foreground mode or it was killed and didFinishLaunchingWithOptions will be invoke again to relaunch the app completly.
Hi i am developing an application with beacons i need to scan for beacons continuously even though app is closed. is there any cordova plugin which is able to run in background, please suggest me sample applications tutorials
Background modes are only a partial answer. You need to have one of or both entries in Info.plist:
Then in some place in your code you need to ask for requestWhenInUseAuthorization or in your case requestAlwaysAuthorization. Then you need to handle this in location manager delegate. I'm doing it like so:
- (void)locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status {
if (status == kCLAuthorizationStatusAuthorizedWhenInUse) {
[self startMonitoringAllRegions];
[self.locationManager startUpdatingLocation];
}
else if (status == kCLAuthorizationStatusNotDetermined) {
[self.locationManager requestWhenInUseAuthorization];
}
}
So somewhere i'm asking to start monitoring for a beacon region then in this method I'm checking if I have permission from the user to do that if not I'm requesting if (the string from the plist will be displayed) if I have I'm starting updating user location.
Hope that will get you going :D
This is possible with native iOS code, but not with Cordova. When you say "scan" beacons, this usually means using the CLLocation ranging APIs, which provid updates of all visible beacons every second. In order to do this continuously in the background, you need to:
Add location as a background mode in your app's .plist filke in XCode as #hasan83 suggests.
Start a background thread to keep your app running in the background.
Request requestAlwaysAuthorization as #sloik suggests.
I wrote a tutorial and reference app of how to do this. The tutorial mentions this working only for 3 minutes, but with the background mode of location, the 3 minute restriction goes away. Be aware that adding this background mode may make it more difficult to get your app approved for the AppStore. Apple must approve of the reasons for this background use of location services.
The reason you can't do this with Cordova is because JavaScript must run to collect the beacon data returned by the ranging APIs. This works in the foreground, but not in the background, because Cordova relies on a WebView being in the foreground to execute JavaScript code. In the background, your custom code just cannot run.
Allow UIBackgroundModes for location updates in the project plist
file.
Select the project at the left pane, then select Capabilities and expand the Background Modes list and select the Location updates option:
Try this plugin for using iBeacons. There seem to be a few plugins for keeping apps running in the background, e.g. this one. I doubt you'll find both functions in the same plugin.
My CLLocationManager starts when the user first enters the app. I am initializing it like this:
self.locationManager = [[CLLocationManager alloc] init];
self.locationManager.delegate = self;
self.locationManager.desiredAccuracy = kCLLocationAccuracyBest;
self.locationManager.distanceFilter = kDistanceFilter;
self.locationManager.headingFilter = kHeadingFilter;
And I am using geofencing.
I have defined in my .plist the required background modes.
If I test the app in the simulator using a gpx file, it works fine. So far, so good.
However, I read in these two links:
Start Location Manager in iOS 7 from background task
Background Location Services not working in iOS 7
That in iOS7, when the location does not change for a couple of minutes then the CLLocation goes to sleep.
My question is that:
I do not call didUpdateLocation at all, I only want the didEnterRegion, or didExitRegion. Theoretically, will it work , even the app is killed or user Location has not changed in the last hour and then decides to move?
There are a few elements in your questions:
1- In order not to rehash what is in a previous answer, I would first look at my answer at this link. It will help you resolve around the core location updates stopping if the user stops moving and how you can modify that behaviour:
iOS7 Core Location not updating
2- If the user kills an app in iOS7 (by flicking it up in the app switcher), the app will not restart and as such neither your location updates nor region monitoring will restart after the phone is restarted. On the other hand, if the app is killed because of memory pressures, iOS will restart the app and relaunch your location updates and region monitoring in the background. Also, if you reboot the phone, region monitoring will restart automatically. You obviously must have the background mode enabled.
3- Now you are talking about regions in your questions so I am assuming you have implemented CLCircularRegion. If not, that should be the first step and then "startMonitoringForRegion". In your case, even if the user has stopped moving for a while and then started moving, the app will be awaken/delegate called when the app enters or exit a region.
4- Make sure to use locationManager: didDetermineState: forRegion: as well. This will allow the app to determine if it is inside or outside of the region at start. This is useful when you think of an app being launched and no region crossing has happened but it is important to know whether the user is inside or outside of the region.
Hope this helps