Long running background task in iOS - ios

I know this is a very common question. I have read many answer but not found out the appropriate answer for me. That's why I post this question and hope someone will show me how to fix my code.
I have function startUpdate to update location using CLLocationManager. In applicationDidEnterBackground method, I write something like below:
[self startUpdate]; // position1
NSLog(#"applicationDidEnterBackground");
__block UIBackgroundTaskIdentifier bgTask;
bgTask = [application beginBackgroundTaskWithExpirationHandler:^{
[application endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
}];
[self startUpdate]; // position2
// Start the long-running task and return immediately.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[self startUpdate]; // position3
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:#"http://google.com"]];
});
I tried to put [self startUpdate] at one of three positions (position1, 2, 3) and sometime it works, sometime not, and I do not know why.
In case it works, updating just run in 3 minutes event. If I call startUpdate when app is in foreground, then put app to background, updating will last 15 minutes with real device, and more than 1hour with simulator ( I don't know exactly, after 1 hour, i thought it would last forever then I stop testing). So what is different between: startupdate in foreground-> go to background vs startupdate in background; simulator vs real device?
right after position3, I called following line to open safari
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:#"http://google.com"]];
But it does not work. So what types of task can be executed here, in background?
I need to keep a service running forever. Some search results say that it's impossible. But some familiar apps did that (Facebook,... keeps service to receive notifications). How can they do that?
Thank you so much,

The exact behaviour of location services has been clarified in the latest update to the Core Location documentation. It now states -
The standard location service delivers events normally while an app is
running in the foreground. When your app is in the background, this
service delivers events only when the location-updates background mode
is enabled for the app. This service does not relaunch iOS apps that
have been terminated.
The significant location change service delivers events normally while
an app is running in the foreground or background. For a terminated
iOS app, this service relaunches the app to deliver events. Use of
this service requires “Always” authorization from the user.
So, it seems that for the best chance of continuing to receive location updates in the background you should switch to significant location change monitoring once you move to the background state and restore full location monitoring once you return to the foreground.

Related

Automatic ON/OFF the GPS tracking in an app running in Background

How can i stop and start the GPS tracking when an application running in background. i Can able to stop tracking in the application, but didn't got any way to start the application. I looking for answers for followings.
1.whether i can use push notification silently to start GPS tracking.
2.I tried with local notification, but it require user interaction to start the process. Is there any best ways to do this.
My problem: Initially i can able start tracking the user location and can stop that after some time in background. I wanted to start the tracking next day. Im looking for a way to start tracking in background.
Actually my application is basically the location tracking application. if the launches the application, the app starts tracking until the time 8:00 pm (stops automatically). Again started tracking next day 8:00 am.
If you started location updates while your app is running in background, these updates will not last longer than whatever time iOS granted for your background task (currently 180 sec).
Location updates must be started while the app is active, doing this ensures the updates keep coming even after your app goes to background, assuming you configured your app background modes properly - see Capabilities/Background Modes/Location updates in Xcode.
Even though, if your app is terminated, the delivery of new location events stops altogether.
If you want to temporary suppress standard location updates, please use
allowDeferredLocationUpdatesUntilTraveled:timeout:
When updates are deferred, GPS receiver is still powered, but no CPU activity happens, as the receiver will not trigger events. I did not test power consumption in this mode myself, but people mention ~2% battery drain per hour.
Have you tried this
-(void)applicationDidEnterBackground {
[self.locationManager stopUpdatingLocation];
UIApplication* app = [UIApplication sharedApplication];
bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
[app endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
}];
self.timer = [NSTimer scheduledTimerWithTimeInterval:intervalBackgroundUpdate
target:self.locationManager
selector:#selector(startUpdatingLocation)
userInfo:nil
repeats:YES];
}
Set intervalBackground Update to 1 day

How to run location updates in background continuously in ios

UIApplication* app = [UIApplication sharedApplication];
_backgroundTask = [app beginBackgroundTaskWithExpirationHandler:^{
[app endBackgroundTask:_backgroundTask];
_backgroundTask = UIBackgroundTaskInvalid;
}];
[self startTimer];
NSLog(#"backgroundTimeRemaining: %.0f", [[UIApplication sharedApplication] backgroundTimeRemaining]);
-(void)startTimer
{
self.updateTimer = [NSTimer scheduledTimerWithTimeInterval:60.0
target:self
selector:#selector(repeatedMethod)
userInfo:nil
repeats:YES];
}
I'm able to run successfully when my app is in foreground but when I come to background if
backgroundTimeRemaining:10 it is running for 3 minutes but when backgroundTimeRemaining:176
it is running continuously. One thing I did not understand is why backgroundTimeRemaining is showing different numbers.How to run process continuously in Background. One thing I need to mention is my background process contains location updates. I'm very new to ios. Any help would be appreciable.
You need to set below key values in your app's info.plist file
Required background modes : App registers for location updates!
As shown in below image and implement you location manager delegate methods in AppDelegate so that its there even your app is in any view controller.
Setting the above key value in info.plist will tell ios to enable background mode for location services.
Read "Getting Location Events in the Background (iOS Only)" in:
https://developer.apple.com/library/ios/documentation/userexperience/conceptual/LocationAwarenessPG/CoreLocation/CoreLocation.html#//apple_ref/doc/uid/TP40009497-CH2-SW10
Basically your app needs to ask for permission to have a background mode for this, and use the location APIs rather than a time. You'll do some setup (get default location manager, set a delegate), call "startUpdatingLocation" from the CLLocationManager class, and then get periodic callbacks with new locations.
Keep in mind you'll drain the user's phone battery pretty fast if you do this, and Apple will reject your app from the App Store unless you give a disclaimer about this.
Set your CLLocationManager's pausesLocationUpdatesAutomatically to NO. It may help you.
For more info check Apple's Location manger documentation.

How can I run an iOS voip app in the background without external signaling?

I'm developing a voip app for iPad. I know there are similar questions, but none of the offered solutions have worked so far. I already know that I have to set the voip flag in the info.plist file (I used xcode for that). I have also set the "application does not run in the background" to "no" (who made up that name?!?). I also configured the socket as voip using the following two lines:
CFReadStreamSetProperty(readStream, kCFStreamNetworkServiceType, kCFStreamNetworkServiceTypeVoIP);
CFWriteStreamSetProperty(writeStream, kCFStreamNetworkServiceType, kCFStreamNetworkServiceTypeVoIP);
I read through the documentation and countless posts on SO and other forums, and there seem to be a few ways to get an app to run in the background forever. I have tried the following:
Start a long running background task, and restart the task when it fires. It was explained here on SO somewhere, but I can't find the post anymore so here is the pseudocode:
expirationHandler = ^{
if (inBackground) {
[[UIApplication sharedApplication] endBackgroundTask:bgTask];
bgTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:expirationHandler];
}
};
inBackground = true;
bgTask = UIBackgroundTaskInvalid;
bgTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:expirationHandler];
// Start the long-running task and return immediately.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// inform others to stop tasks, if you like
[[NSNotificationCenter defaultCenter] postNotificationName:#"MyApplicationEntersBackground" object:self];
inBackground = true;
while (inBackground) {
//send a keep alive to my server
sleep(5);
}
});
The second thing I tried was to use setKeepAliveTimeout like this:
[[UIApplication sharedApplication] setKeepAliveTimeout:600 handler:^{
//send a keep alive to my server
}];
The first one seems to work very well (note that battery life and app-store approval are of no concern to me), but only when the application runs from xcode. When I put the app on the device and run it without debugger, the app stays alive for about 3 minutes and then it dies.
The second one seems to be how it is supposed to be, but my problem with it is that it has a minimum time of ten minutes. My server closes the connection after ten minutes of inactivity and setKeepAliveTimeout seems to be a bit inaccurate, so sometimes it is off by half a second or so (I've experienced 2 seconds one time). This means that once every ten minutes there is a chance that my session to the server is closed.
I use a protocol called XIMSS, used by the Communigate Pro server platform. Most voip apps seem to use SIP, which can send keep alive packets from the server, but that is not a option for me. So how can I make sure my app always wakes in time to send a keep alive? Is there anything that has an interval smaller than ten minutes?

iOS best practice for background location update

Overview
My compagny is asking me to release an application that can check location of the device every two hours. The app would send these location data through a TCP/IP socket to my server and then receive information accordingly to these very data (straight away, and through the same TCP/IP socket). So I'm not trying to make my application running in background mode continuously (which, actually, seems to be a hot topic in iOS, and it's also not suitable for my project).
Question
In order to be reliable, what is the best practice to achieve this? So, I would like to know:
Since my app is suspended (= inactive), does Apple allow to open a socket to send location when it's woken up by didUpdateToLocation?
How long do I have to perform my send/receive task via my socket?
Should I create a real background task with beginBackgroundTaskWithExpirationHandler and use the 10 minutes allowed by Cocoa to perform my send/receive task?
It is possible (and allowed by Apple) to ask for a 10 mins background task every 2 hours, without human interaction (ie. the user should re-open the app, etc)?
What I achieved/found so far
I added the location key in my Info.plist to be able to run the didUpdateToLocation handler when my app is inactive.
I am able to send and received data through a socket I have opened when my application was in foreground (= active).
I tried to check the backgroundTimeRemaining when didUpdateToLocation is called. I got a very large result number, which seems to be normal because, at this point, the applicationState is not in UIApplicationStateBackground but in UIApplicationStateActive.
These points are not very clear in the official documentation, and I did not found topics related to my specific case.
Thanks for your help.
According to Apple's documentation, you can achieve these by using a very similar approach to the one you described. What I would do is something similar to what is explained in this post by mindsizzlers:
As a recommendation, turn on significant location updates when the app enters in background, so you save battery. You can do this when the app goes to background:
- (void) applicationDidEnterBackground:(UIApplication *) application
{
// Create the location manager if you do not already have one.
if (nil == locationManager)
locationManager = [[CLLocationManager alloc] init];
locationManager.delegate = self;
[locationManager startMonitoringSignificantLocationChanges];
}
Then, the system will wake your app when location changes, as explained in the documentation.
If you leave this service running and your app is subsequently suspended or terminated, the service automatically wakes up your app when new location data arrives. At wake-up time, your app is put into the background and given a small amount of time to process the location data. Because your app is in the background, it should do minimal work and avoid any tasks (such as querying the network) that might prevent it from returning before the allocated time expires. If it does not, your app may be terminated.
In order to avoid the action of sending the new location to the server from being highly unreliable (it may work sometimes) you should tell iOS in advance that you are doing a background task that should be allowed to run to completion.
Change your location manager delegate (didUpdateToLocation) to handle background location updates.
- (void) locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation
fromLocation:(CLLocation *)oldLocation
{
if ([UIApplication sharedApplication].applicationState == UIApplicationStateBackground) {
// Send the new location to your server in a background task
// bgTask is defined as an instance variable of type UIBackgroundTaskIdentifier
bgTask = [[UIApplication sharedApplication]
beginBackgroundTaskWithExpirationHandler:
^{
[[UIApplication sharedApplication] endBackgroundTask:bgTask];
}];
// Make a SYNCHRONOUS call to send the new location to our server
// Close the task
if (bgTask != UIBackgroundTaskInvalid) {
[[UIApplication sharedApplication] endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
}
} else {
// Handle location updates in the normal way
}
}
This way, you will not need to have the timer running. Since you will be waken up automatically and sending the updated location to your server every time it changes significantly.
Of course, if you want to make sure this happens in a specific interval, you can still go with the approach of setting a timer to start receiving location updates and as soon as you get it, you send it to the server. Take a look to this post talking about Background Modes in iOS (section: Receiving Location Updates) and this other questions to see how to do this in detail.

How is it possible to ensure a constant continuous update of location when iOS app is in the background?

One requirement for our iOS app is to monitor location changes in the background. We have implemented "locationManager startMonitoringForRegion:region", unfortunately we realised when testing the app thoroughly that it is too little precise to fulfil our needs.
We then used "locationManager startUpdatingLocation" to increase accuracy of location. In the foreground everything worked out quite good, this location precision is pretty much what we want.
Now the problem is when the app is in the background, the event "didUpdateLocations" is not called on regular basis. We observed this by looking into log file. What happens is pretty much always the same, the first 15 minutes the app updates the location regularly and then suddenly abruptly stops. After about a 5 minute break it carries on with updating the location for again about 15 minutes. And this continues on in the same way.
We probably made the mistake in the implementation in background handling.
UIApplication *app = [UIApplication sharedApplication];
UIBackgroundTaskIdentifier bgTask = 0;
bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
if(rootViewController._selectedLocation == nil)
{
NSLog(#"selected location is nil and app entered background");
[rootViewController.locationManager stopUpdatingLocation];
}
else
{
NSLog(#"selected location is not nil and app entered background");
[rootViewController.locationManager startUpdatingLocation];
}
[app endBackgroundTask:bgTask];
}];
This seem to be called about every 20 minutes and the location is updated again. What are we doing wrong and how can we get a regular location update without any interruptions? We tested this with iPhone 5 and also important to know it was stationary.
You should add to your application plist file the following value:
Required background modes
It is an array telling the system what you app need when in background. For the position at item 0, you should inform the value:
App registers for location updates
In case of doubt, you can see in this link what I'm talking about: iOS Multitasking: Background Location

Resources