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.
Related
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 am trying to run some code during the applicationWillResignActive when the user opens the task switcher and it has worked fine until I began using bluetooth in my app.
When bluetooth tries to connect to a device it shows an alert window asking if the user wants to pair the device. This alert is enough to trigger the applicationWillResignActive method and then runs my code for when the app is being navigated away from (task switcher). This causes a large problem since the code I intend to run when switching away, turns off some much needed functionality within the actual app. So once they press "pair" or "cancel" on that alert, all of my app stops functioning as it should because the app has lost focus.
I have tried to detect the state of the application during this time with this... NSUInteger state = [[UIApplication sharedApplication] applicationState]; thinking of course that it would be considered active when the alert pops up and inactive when in the task switcher. However, this was not the case it shows up as active for both use cases.
Update #1
The question...
How can I differentiate in the application between the app causing a system level inactive focus state like running code to connect to bluetooth, versus the user causing the system level inactive focus like double tapping the home button? All in the efforts to distinguish what is causing the applicationWillResignActive method to fire.
Update #2
The intention of this functionality is to set a flag in NSUserDefaults when bluetooth connects to the device. This flag is being "observed" and used to trigger the changing of view controllers to a page related to this new BT connection. When the user double presses the home button and moves to task switcher I turn off BT and switch to iBeacon so I can notify of events. All is well with this current implementation all bar 1 use case.
If the user hasn't yet connected to the BT device and it connects for the first time and that pairing alert comes up it fires the applicationWillResignActive method just the same as double tapping the home button does. In this method the code then checks for that NSUserDefaults flag to see if it switched on (which by this time it is because the BT has already reached the CBCentralManager's didConnectPeripheral method and turned it on) and if it's on, it turns off BT and switched to scanning for iBeacon. Because the app is still open this obviously causes problems. The app is running so the user see's the BT connect, the new view slide in, the pairing alert come up, then the new view slide right back out and iBeacon starts sending notifications intended for when the user is in the task switcher.
I already have this exact functionality happening in the applicationWillEnterBackground method so that's not the answer. I need to have a way of saying "the app is running right now and we've received an alert instead of double tapping home, so please don't turn off BT and turn on iBeacon yet"
Two possible solutions:
1. The answer may lie in this statement:
When bluetooth tries to connect to a device it shows an alert window asking if the user wants to pair the device.
Your app must do something to cause this alert to appear. You could set a Date field to the current time in your AppDelegate when this happens, and then when you get a call to applicationWillResignActive you can compare that timestamp to the current time, and if it is < 1 second or so, you have a pretty good clue that the bluetooth dialog went up.
Of course, this is not foolproof. As #danh notes in his comment, the design of iOS makes this really difficult. You won't know for sure if the bluetooth dialog went up, or if the user or OS just happened to bring something else to the foreground at the same time. What's more, it's always possible that even if the bluetooth dialog comes up, the user might decide at that very moment to go check his or her email or start browsing Facebook. In that case, it is both true that the bluetooth dialog is what sent your app to the background, AND the user navigated away from the app. Unfortunately, iOS doesn't really give you a way to differentiate the two.
2. You might use a background task to handle your cleanup logic.
You can request up to 180 seconds of background running time after the call to applicationWillResignActive, so you could defer your cleanup tasks until say 175 seconds have passed since your app is resigned to the background. If the user doesn't come back within 3 minutes, it's probably time to do this cleanup anyway. My blog post here shows the basics of setting up a background task. It is specifically targeted to extending beacon ranging time, but you can put whatever logic you want inside the background code block like this:
- (void)extendBackgroundRunningTime {
if (_backgroundTask != UIBackgroundTaskInvalid) {
// if we are in here, that means the background task is already running.
// don't restart it.
return;
}
NSLog(#"Attempting to extend background running time");
__block Boolean self_terminate = YES;
_backgroundTask = [[UIApplication sharedApplication] beginBackgroundTaskWithName:#"DummyTask" expirationHandler:^{
NSLog(#"Background task expired by iOS");
if (self_terminate) {
[[UIApplication sharedApplication] endBackgroundTask:_backgroundTask];
_backgroundTask = UIBackgroundTaskInvalid;
}
}];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(#"Background task started. Waiting 175 seconds before cleanup.");
[NSThread sleepForTimeInterval:175];
//TODO: perform cleanup code if app is not in the foreground by now
});
}
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.
I have a Cordova app that records user's trips. It works fine in the background, and I'd like to get it automatically restarted if it's killed for some reason (user swipes it away, phone restarts, etc.).
I'm monitoring a region, and the app partially restarts when the user enters/exits from that. 'Partially' here means the app only runs in the background - Cordova loads my app's plugins, but it never loads the WebView.
For this partial restart, I'm listening for UIApplicationDidFinishLaunchingNotification with UIApplicationLaunchOptionsLocationKey from my plugin's pluginInitialize method and starting location services right away to stay running.
Why doesn't the WebView load? Is there something I need to do to trigger the next stage of initialization when started by iOS?
EDIT 2016/3/7
I did some research and debugging. Apparently, iOS starts my app in the background; here's what I get when auto-started this way vs. a manual start, from the logging line:
NSLog(#"Launched in: bg:%d active:%d inactive:%d", state == UIApplicationStateBackground, state == UIApplicationStateActive, state == UIApplicationStateInactive);
Launched in: bg:1 active:0 inactive:0
Launched in: bg:0 active:0 inactive:1
I verified this code was being reached in both cases (from cordova-ios' v.3.9.2 (Cordova v.5.4.1) CDVViewController:
NSURLRequest* appReq = [NSURLRequest requestWithURL:appURL cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:20.0];
[self.webView loadRequest:appReq];
I see this load finish in the manual case, but not the auto-start case, with the log line:
Finished load of: file:///var/mobile/Containers/Bundle/Application/[device id]/[app name].app/www/index.html
Why would this load fail when auto-started?
I ended up using an Apple support ticket; their answer is basically "you can't load a web view when started that way":
If I understand correctly, you are expecting your app to visibly launch to the foreground when you get a location update.
that is not how it works at all. Your app will be launched into the background. If you see the UIApplicationLaunchOptionsLocationKey, only your app delegate and the location manager’s delegate class will be initialized. No views will be initialized whatsoever.
Once an app is launched in the background, it is expected to process the data for a few seconds silently and then yield back to the system. It is not possible to load a web view, or any kind of UI at this stage.
The only user interaction that would be possible is to schedule a local notification with a message to the user to launch the app. If the user taps the notification, your app will be fully launched.
Only then you can load your web view.
I am using swift 2 and Xcode7 for iOS9. I want to know if I can maintain a function (that checks for something to delete) running "forever" even if the user kills the app?
I am deleting contacts from the contact list according to some rules and time. It is running ok, but just with the app opened or in second plan. I want to make this app capable to delete those contacts even when the user kills it.
You can use background thread when user opens the app. But if the app will be terminated, there is no option to run functions.
Look for the app lifecycle here and redesign your architecture: https://developer.apple.com/library/ios/documentation/iPhone/Conceptual/iPhoneOSProgrammingGuide/TheAppLifeCycle/TheAppLifeCycle.html
If the user kills the app it is no longer running, therefore your code is no longer running. There is no such state that your code/app can be in where this is possible.
By "kill", I don't mean "background". Backgrounding an app is different. Check Apple's docs on the different app states (see m.albin's answer) as well as various strategies for handling those app states.
func applicationWillTerminate(_ application: UIApplication) {
// Called when the user discards a scene session.
// If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.
// Use this method to release any resources that were specific to the discarded scenes, as they will not return.
print("Application Will Terminate")
}