Run network connection in background mode - ios

I have this code who send data and receive even when the application is in background mode (minimized app):
MyViewController.m
-(void)viewDidAppear:(BOOL)animated{
[self doUpdateEvenAppMinimized];
}
- (void) doUpdate{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[self beginBackgroundUpdateTask];
[self sendFilesToServer];//Inside this method have a sleep that call every 5 minutes
//The code used in sendFilesToServer is the same in this website https://dcraziee.wordpress.com/2013/05/29/how-to-upload-file-on-server-in-objective-c/
//[self endBackgroundUpdateTask];//This method is forever...so I not need to call this line
});
}
- (void)beginBackgroundUpdateTask{
self.backgroundUpdateTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
[self endBackgroundUpdateTask];
}];
}
- (void) endBackgroundUpdateTask{
[[UIApplication sharedApplication] endBackgroundTask: self.backgroundUpdateTask];
self.backgroundUpdateTask = UIBackgroundTaskInvalid;
}
The documentation says that the maximum time is 10 minutes, and to remove it I use the concept of Implementing Long-Running Tasks, For that I select my project > capabilities > Background Modes (Turn On) > External accessory communication (Checked).
With these steps, my application will be exempt from the 10 minutes?

Trying to circumvent the rules to run in the background sounds like the wrong approach. Consider using NSURLSession for long running networking operations that are not tied to the lifetime of your app.

Related

Getting gyro updates in background state iOS Swift [duplicate]

I am trying to collect coreMotion acceleration data in the background for longer than 10 minutes. This must be possible since apps like Sleep Cycle do this.
I just want to make sure this is allowed though, since it does not seem to be one of these:
Apps that play audible content to the user while in the background, such as a music player app
Apps that record audio content while in the background.
Apps that keep users informed of their location at all times, such as a navigation app
Apps that support Voice over Internet Protocol (VoIP)
Apps that need to download and process new content regularly
Apps that receive regular updates from external accessories
Apps that implement these services must declare the services they support and use system frameworks to implement the relevant aspects of those services. Declaring the services lets the system know which services you use, but in some cases it is the system frameworks that actually prevent your application from being suspended.
However, I have tried following these steps to get a background task, but I am thinking there is a better way for CoreMotion:
Header:
UIBackgroundTaskIdentifier bgTask;
Code:
// if the iOS device allows background execution,
// this Handler will be called
- (void)backgroundHandler {
NSLog(#"### -->VOIP backgrounding callback");
UIApplication* app = [UIApplication sharedApplication];
bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
[app endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
}];
// Start the long-running task
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
while (1) {
NSLog(#"BGTime left: %f", [UIApplication sharedApplication].backgroundTimeRemaining);
[self doSomething];
sleep(1);
}
});
- (void)applicationDidEnterBackground:(UIApplication *)application {
BOOL backgroundAccepted = [[UIApplication sharedApplication] setKeepAliveTimeout:600 handler:^{ [self backgroundHandler]; }];
if (backgroundAccepted)
{
NSLog(#"VOIP backgrounding accepted");
}
UIApplication* app = [UIApplication sharedApplication];
bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
[app endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
}];
// Start the long-running task
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
while (1) {
NSLog(#"BGTime left: %f", [UIApplication sharedApplication].backgroundTimeRemaining);
[self doSomething];
sleep(1);
}
});
}
Use this one:
Apps that keep users informed of their location at all times, such as a navigation app
In other words, you make a location manager and tell it to start doing updates. You don't have to do anything with those updates! But as long as this is happening - that is, as long as your app is continuing to do location updates in the background - your app is also allowed to use Core Motion in the background. This is not just a trick; it is official Apple policy as explained in one of the WWDC videos from a couple of years ago.

endBackgroundTask: Causing "Terminated due to signal 9"

My app syncs RSS feeds which takes about 15-30 seconds and for each sync I request beginBackgroundTaskWithExpirationHandler:. In iOS 7 and iOS 8 everything worked perfectly.
Starting with iOS 9 calling [[UIApplication sharedApplication] endBackgroundTask: backgroundTask]; causes the app to crash with Message from debugger:
Terminated due to signal 9.
From my research, signal 9 means the app using too much memory. When I use instruments, the app never goes over 30mb or 40% cpu.
I know that it's from calling endBackgroundTask: because if I don't call it, the app doesn't crash. Yet, once I call endBackgroundTask: the app will crash every time.
I'm not sure what's going wrong here. I've tried everything. Re-writting code, moving code around, commenting out everything except endBackgroundTask:. Any help or insight would be appreciated.
Here's the code:
#interface SyncClass ()
#property (nonatomic) UIBackgroundTaskIdentifier backgroundTask;
#end
-(void)startSync
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[self beginBackgroundUpdateTask];
// I then call my syncing code [syncClass sync];
});
//When sync is done call endBackgroundTask
[self endBackgroundUpdateTask];
}
- (void) beginBackgroundUpdateTask
{
NSLog(#"Background Time:%f",[[UIApplication sharedApplication] backgroundTimeRemaining]);
self.backgroundTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
[self endBackgroundUpdateTask];
}];
}
- (void) endBackgroundUpdateTask
{
[[UIApplication sharedApplication] endBackgroundTask: self.backgroundTask];
self.backgroundTask = UIBackgroundTaskInvalid;
NSLog(#"Ending background task");
}
Answering my own question here. AFNetworking and FMDB happened to be out of date. Updating them through cocoa pods seems to have fixed the problem.

iPhone collecting CoreMotion data in the background. (longer than 10 mins)

I am trying to collect coreMotion acceleration data in the background for longer than 10 minutes. This must be possible since apps like Sleep Cycle do this.
I just want to make sure this is allowed though, since it does not seem to be one of these:
Apps that play audible content to the user while in the background, such as a music player app
Apps that record audio content while in the background.
Apps that keep users informed of their location at all times, such as a navigation app
Apps that support Voice over Internet Protocol (VoIP)
Apps that need to download and process new content regularly
Apps that receive regular updates from external accessories
Apps that implement these services must declare the services they support and use system frameworks to implement the relevant aspects of those services. Declaring the services lets the system know which services you use, but in some cases it is the system frameworks that actually prevent your application from being suspended.
However, I have tried following these steps to get a background task, but I am thinking there is a better way for CoreMotion:
Header:
UIBackgroundTaskIdentifier bgTask;
Code:
// if the iOS device allows background execution,
// this Handler will be called
- (void)backgroundHandler {
NSLog(#"### -->VOIP backgrounding callback");
UIApplication* app = [UIApplication sharedApplication];
bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
[app endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
}];
// Start the long-running task
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
while (1) {
NSLog(#"BGTime left: %f", [UIApplication sharedApplication].backgroundTimeRemaining);
[self doSomething];
sleep(1);
}
});
- (void)applicationDidEnterBackground:(UIApplication *)application {
BOOL backgroundAccepted = [[UIApplication sharedApplication] setKeepAliveTimeout:600 handler:^{ [self backgroundHandler]; }];
if (backgroundAccepted)
{
NSLog(#"VOIP backgrounding accepted");
}
UIApplication* app = [UIApplication sharedApplication];
bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
[app endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
}];
// Start the long-running task
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
while (1) {
NSLog(#"BGTime left: %f", [UIApplication sharedApplication].backgroundTimeRemaining);
[self doSomething];
sleep(1);
}
});
}
Use this one:
Apps that keep users informed of their location at all times, such as a navigation app
In other words, you make a location manager and tell it to start doing updates. You don't have to do anything with those updates! But as long as this is happening - that is, as long as your app is continuing to do location updates in the background - your app is also allowed to use Core Motion in the background. This is not just a trick; it is official Apple policy as explained in one of the WWDC videos from a couple of years ago.

NSURLConnection best practise when enter background

I notice in my app that when application enter background while loading causes the error such as "timeout" or "host name not found" .
It is due to the process that does not allow connection to run in background for a long time.
But that kind of error message make it bad for user experience. So what should I do to cancel the transaction ? Should I just cancel all the connection ? I tried to search the Q&A in SO here but can't find an answer.
For more information, my app use NSURLConnectionDelegate Method. I have a store singleton that manage all connection to my server. NSURLConnection is called and managed in custom object also.
I tried to just [connection cancel] in - applicationDidEnterBackground: but that make the UI broken because I load data to put into UITableViewCell ,etc. Can anyone point to the example to solve this kind of problem?
Updated Code:
- (void)applicationDidEnterBackground:(UIApplication *)application
{
__block UIBackgroundTaskIdentifier backgroundTask; backgroundTask =
[application beginBackgroundTaskWithExpirationHandler: ^ {
[application endBackgroundTask:backgroundTask];
backgroundTask = UIBackgroundTaskInvalid; }]; }
}
Can I just put this code in the appDelegate ? What is the drawback for just doing this versus put the beginBackgroundTaskWithExpirationHandler before the task that I want to keep running in background and endBackgroundTask after that task finished ? My code has one object that deal directly to NSURLConnection.
You are allowed to keep running an NSURLConnection for some period of time after you go into the background. Apple doesn't publish the exact period of time, but it's 10 minutes. See Executing a Finite-Length Task in the Background for details on how to use beginBackgroundTaskWithExpirationHandler: to request more time to complete your download.
In most cases you shouldn't proactively cancel your download. You should wait until the system expires you and then deal with the error at that point. If your download is brief, there's no reason to cancel it (in most cases, the most expensive thing about a connection is setting it up in the first place). If it's a very long download, then the user is going to be annoyed if it doesn't proceed in the background. (This assumes that you're downloading things because the user requested it.)
First, it's better to have this call on the application delegate, so that the View in the NavigationController can be closed. Second, mark beginning of the background processing with beginBackgroundTaskWithExpirationHandler: and end it with endBackgroundTask: like this:
.h:
UIBackgroundTaskIdentifier bgTask;
.m:
- (void)sendPhoto:(UIImage *)image
{
UIApplication *app = [UIApplication sharedApplication];
bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
[app endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
}];
NSLog(#"Sending picture...");
// Init async NSURLConnection
// ....
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
NSLog(#"Picture sent.");
UIApplication *app = [UIApplication sharedApplication];
if (bgTask != UIBackgroundTaskInvalid) {
[app endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
}
}
Also remember one thing its important:
You have 10 minutes before iOS terminates your app. You can check this time with [app backgroundTimeRemaining]

Keep a dispatch queue running for 5 sec after moving to the background

I am using a serial dispatch queue to serialize some network requests when the user moves the app to the background.
- (void)applicationDidEnterBackground:(UIApplication *)application
{
dispatch_queue_t opQ = dispatch_queue_create("com.myapp.network", NULL);
dispatch_async(opQ, ^{
[self sendNetworkData1];
[self sendNetworkData2];
[self sendNetworkData3];
});
}
The problem is that when they run on this queue I have created, the app doesn't stay active even for the 5 seconds it is supposed to.
On the contrary, when I send the same requests outside of a queue, they are being sent for approximately 8 sec. but the app crashes afterwards.
- (void)applicationDidEnterBackground:(UIApplication *)application
{
[self sendNetworkData1];
[self sendNetworkData2];
[self sendNetworkData3];
}
I would also like to write the remaining ones on the disk, so that they can be sent the next time the user opens the app.
What's the best way to implement this?
When the application enters the background if it requires additional time to complete some task you will want to notify the OS of that. The detailed documentation is here: http://developer.apple.com/library/ios/#DOCUMENTATION/iPhone/Conceptual/iPhoneOSProgrammingGuide/ManagingYourApplicationsFlow/ManagingYourApplicationsFlow.html. Here's a quick and dirty patch.
- (void)applicationDidEnterBackground:(UIApplication *)application
{
__block UIBackgroundTaskIdentifier backgroundTask; //Create a task object
backgroundTask = [application beginBackgroundTaskWithExpirationHandler: ^ {
[application endBackgroundTask:background_task];
backgroundTask = UIBackgroundTaskInvalid; //Set the task to be invalid
}];
dispatch_queue_t opQ = dispatch_queue_create("com.myapp.network", NULL);
dispatch_async(opQ, ^{
[self sendNetworkData1];
[self sendNetworkData2];
[self sendNetworkData3];
[application endBackgroundTask:background_task];
backgroundTask = UIBackgroundTaskInvalid; //Set the task to be invalid
});
}
The bottom line is that you notify that the application needs to run in the background with beginBackgroundTaskWithExpirationHandler: THen when your done you call endBackgroundTask: to notify the OS that you are finished processing in the background. And finally make sure that you reset the backgroundTask variable to UIBackgroundTaskInvalid.

Resources