I need to run my long running task until it is finished and it does not matter if the application is gone to the background.
So I have subclass of NSOperation, and I added this NSOperation to NSOperationQueue. In the main function of the NSOperation I have:
- (void)main {
UIApplication *app = [UIApplication sharedApplication];
UIBackgroundTaskIdentifier taskId = 0;
taskId = [app beginBackgroundTaskWithExpirationHandler:^(void) {
[app endBackgroundTask:taskId];
}];
NSManagedObjectContext *searchContext = [NSManagedObjectContext createSearchTempContext];
[searchContext performBlockAndWait:^{
NSArray *array = //Get Array data from CoreData;
if (array.count > 0) {
//Long running task
}
if (taskId != UIBackgroundTaskInvalid) [app endBackgroundTask:taskId];
}];
Is it all I need to run in the background? Or something I missed?
UIBackgroundTaskIdentifier would only be able to give you some extra time to complete your task, it would allow your app to remain alive for maximum 10 mins, then it would go in suspended state, For an APp to run in background there are only few ways possible which are listed as follows
Play audio:
Receive location updates:
Perform finite-length tasks:
Process Newsstand Kit downloads:
Provide Voice-over-IP (VoIP) services:
If your app dosent use any of these then it will be suspended after 10 mins regardless of all your operations
Related
I need to run some code when the user enters a background state. The default time I was getting for when I entered the background on iOS 9 was 10 seconds. I needed a bit more than that, so I found that this code will extend the time to 3 minutes:
- (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");
while (true) {
NSLog(#"background time remaining: %8.2f", [UIApplication sharedApplication].backgroundTimeRemaining);
[NSThread sleepForTimeInterval:1];
}
});
}
However, my task doesn't need all of this extra time, and I would like to conserve as much battery as possible. Is there any way to use this or similar code to get 1 minute of background time, or some other value between 10 and 180 seconds?
You should call endBackgroundTask: once you are done with your background processing. If you are done before the 3 minute time allotted to you, that should end your background processing early and let iOS suspend you. I haven't tested it to verify, but that's what the docs suggest.
I have added data fetching operations to NSOperationQueue .During the process, I just put the device to sleep mode and the process gets stopped. I surfed stack overflow an get some basic ideas.I need help to continue my fetching process without any interruption when device moves to sleep mode.Help appreciated !!
NSManagedObjectContext *managedObjectContext=((AppDelegate *)[[UIApplication sharedApplication] delegate]).managedObjectContext;
NSOperationQueue *downLoadQueue=((AppDelegate *)[[UIApplication sharedApplication] delegate]).downloadqueue;
//Fetch operation
if([fetchqueue count]>0)
{
Queue *queue=[fetchqueue objectAtIndex:0];
queue.status=#"INP";
[managedObjectContext performBlockAndWait:^{
NSError * error = nil;
if (![managedObjectContext save:&error]){
NSLog(#"Unresolved error while loading3");
}
}];
DownloadOperation *downloadOp=[[DownloadOperation alloc]init];
downloadOp.queue=queue;
[downLoadQueue addOperation:downloadOp];
}
}
The downLoadQueue started its execution. It is fetching data from server meanwhile the device goes to sleep and the execution stops. I don't know how to continue in this block applicationDidEnterBackground. How can I get my lengthy downloading process during sleep mode?
Also tried this one..But the operation was not resumed.
//code follows
- (void)applicationDidEnterBackground:(UIApplication *)application
{
// Start the long-running task and return immediately.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSOperationQueue *downLoadQueue;
downLoadQueue=((AppDelegate *)[[UIApplication sharedApplication] delegate]).downloadqueue;
[downLoadQueue waitUntilAllOperationsAreFinished];
});
}
Please suggest any ideas.
I think the only way to do this is background task. Take a look at apple docs about this. There are also a lot of answers on stackowerflow how to implemet such feature.
I think in your case you can try next code to execute NSOperations a in backgraund task:
- (void)applicationDidEnterBackground:(UIApplication *)application
{
bgTask = [application beginBackgroundTaskWithName:#"MyTask" expirationHandler:^{
// Clean up any unfinished task business by marking where you
// stopped or ending the task outright.
[application endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
}];
// Start the long-running task and return immediately.
//Start your NSOperationQueue if it's not executing
//Lock current thread while operations are executing
[queue waitUntilAllOperationsAreFinished];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// Do the work associated with the task, preferably in chunks.
[application endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
});
}
- (void)applicationDidEnterBackground:(UIApplication *)application
{
[_dataStore saveChanges];
[_sync syncWithServerWithDate:[NSDate dateWithTimeIntervalSince1970:timestamp]];
}
-(void)syncWithServerWithDate:(NSDate *)date
{
void(^postCompletionBlock)(FTjsonEvents *obj, NSError *error) = ^(FTjsonEvents *serverEvents, NSError *error) {
...
NSLog(#"Post Completion block finished!");
};
void(^completionBlock)(FTjsonEvents *obj, NSError *error) = ^(FTjsonEvents *serverEvents, NSError *error) {
....
NSLog(#"Fetch finished!");
[self postRecordsSinceLastServerSyncTimestamp:[date timeIntervalSince1970] WithCompletion:postCompletionBlock];
};
NSLog(#"Syncing data...");
[self fetchRecordsByDate:date WithCompletion:completionBlock];
}
I would like to sync with the server to fetch and post the latest data to.
Since this is happening via async completion blocks, it seems that my classes get garbage collected once I press home button. The sync never reaches the server.
However the local coredata is easily saved when doing this: [_dataStore saveChanges];
Is there a way to keep the async sync alive in the background until its completed?
Your implementation of applicationDidEnterBackground: has approximately five seconds to perform any tasks and return. If you need additional time to perform any final tasks, you can request additional execution time from the system by calling beginBackgroundTaskWithExpirationHandler:. In practice, you should return from applicationDidEnterBackground: as quickly as possible. If the method does not return before time runs out your app is terminated and purged from memory.
You should perform any tasks relating to adjusting your user interface before this method exits but other tasks (such as saving state) should be moved to a concurrent dispatch queue or secondary thread as needed. Because it's likely any background tasks you start in applicationDidEnterBackground: will not run until after that method exits, you should request additional background execution time before starting those tasks. In other words, first call beginBackgroundTaskWithExpirationHandler: and then run the task on a dispatch queue or secondary thread.
Here's an example implementation:
#interface XXAppDelegate (BackgroundStuff)
#property (nonatomic, assign) UIBackgroundTaskIdentifier backgroundTask;
#end
#implementation
- (void)applicationDidEnterBackground:(UIApplication *)application
{
// Request additional background time.
self.backgroundTask = [application beginBackgroundTaskWithExpirationHandler:^{
[application endBackgroundTask:self.backgroundTask];
}];
// Start background task.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
//Background code goes here
//Cleanup background task id
[application endBackgroundTask:self.backgroundTask];
self.backgroundTask = UIBackgroundTaskInvalid;
});
}
#end
I am using robbiehanson/CocoaAsyncSocket for async socket, when my iPad goes into background state i disconnect and close all the ports, but some times when iPad awakes from sleep it crashes, happened same with simulator when mac goes to sleep mode. Once i got crash log as [AsyncSocket close] unrecognized instance send to selector.
I am not able to find a solution for this, can any one help me on this. Below is my code for disconnecting.
- (void) enterBackground
{
if (self.discoveryUdpSocket!=nil)
{
self.discoveryUdpSocket.delegate = nil;
[self.discoveryUdpSocket close];
}
self.discoveryUdpSocket.delegate = nil;
self.discoveryUdpSocket = nil;
}
Have you tried to extend time when entering background mode to allow socket framework complete its job?
I mean to add in the app delegate a method:
-(void)applicationDidEnterBackground:(UIApplication *)application {
UIApplication *app = [UIApplication sharedApplication];
__block UIBackgroundTaskIdentifier bgTask = 0;
bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
NSLog(#"Times up!");
[application endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
}];
// Let the async socket to complete its job and finally close the connection
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(#"Finishing job and closing async socket, time remaining=%f", [app backgroundTimeRemaining]);
// Calling your background routine
[self enterBackground];
[app endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
});
}
This is a good way to complete important job just before app is suspended. You have max 180 secs.
I have have an upload queue running using ASIHTTP request. When the user presses the home screen and the app goes into the background I would like this operation to continue. From the documentation I can see how I would call a new task could be called to run in the back ground, but can't quite see how I would flag an already running task to continue.
this example is bracketed for pre-4.0 compatibility:
UIApplication *app = [UIApplication sharedApplication];
if ([app respondsToSelector:#selector(beginBackgroundTaskWithExpirationHandler:)]) {
backgroundTaskIdentifier = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
dispatch_async(dispatch_get_main_queue(), ^{
if (backgroundTaskIdentifier != UIBackgroundTaskInvalid)
{
// you took too long - clean up what you can, then …
[app endBackgroundTask:backgroundTaskIdentifier];
backgroundTaskIdentifier = UIBackgroundTaskInvalid;
}
});
}];
}
// start HTTP request …
when you complete your process, you should call endBackgroundTask: to let the app know