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.
Related
I have some NSOperations that are started regularly in my application. They should complete even when the the application is put to background. For this, I'm using the beginBackgroundTaskWithExpirationHandler method.
Am I supposed to use the beginBackgroundTaskWithExpirationHandler/ endBackgroundTask: every time I start my task even if the app is not going to background? Or am I supposed the call the begin/end methods only when I detected a UIApplicationDidEnterBackgroundNotification?
Option 1: Use background task every time
/**
* This method is called regularly from a NSTimer
*/
- (void)processData
{
__block UIBackgroundTaskIdentifier operationBackgroundId = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
[[UIApplication sharedApplication] endBackgroundTask:operationBackgroundId];
operationBackgroundId = UIBackgroundTaskInvalid;
}];
NSOperation *operation = ...
[self.queue addOperation:operation];
operation.completionBlock = ^{
[[UIApplication sharedApplication] endBackgroundTask:operationBackgroundId];
operationBackgroundId = UIBackgroundTaskInvalid;
};
}
Option 2: Use background task only when the application is about to go to background
/**
* This method is called regularly from a NSTimer
*/
- (void)processData
{
NSOperation *operation = ...
[self.queue addOperation:operation];
}
- (void)applicationDidEnterBackground:(NSNotification *)notification
{
__block UIBackgroundTaskIdentifier operationBackgroundId = [[UIApplication sharedApplication] beginBackgroundTaskWithName:#"EnterBackgroundFlushTask" expirationHandler:^{
[[UIApplication sharedApplication] endBackgroundTask:operationBackgroundId];
operationBackgroundId = UIBackgroundTaskInvalid;
}];
// wait for all operations to complete and then
// let UIApplication know that we are done
[[UIApplication sharedApplication] endBackgroundTask:operationBackgroundId];
}
Answering my own question. From the Apple Docs:
You do not need to wait until your app moves to the background to
designate background tasks. A more useful design is to call the
beginBackgroundTaskWithName:expirationHandler: or
beginBackgroundTaskWithExpirationHandler: method before starting a
task and call the endBackgroundTask: method as soon as you finish. You
can even follow this pattern while your app is executing in the
foreground.
Other Apple API reference:
You should call this method at times where leaving a task unfinished might be detrimental to your app’s user experience.
You can call this method at any point in your app’s execution.
Option2 is correct option.Here is code from Apple document for your reference.
- (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.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// Do the work associated with the task, preferably in chunks.
[self processData];
[application endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
});
}
Apple developer Guide
In my app , I am triggering many GET/POST request and handling the response. But just after initiating the request if I lock the device then on resuming the app after some seconds the response doesn`t come.Is there any way to let the request active even in the background or on the device locked state till the response comes. I went through the Background Execution Docs in iOS but that seems to be used if want to download or do something in background.Not fulfilling my scenario.
If you register a background task for each request, your app will keep running even when the device is locked, as long as a request is still active. The system only allows a maximum of 4 minutes though, so your requests mustn't take too long.
When you start a GET/POST request, do this:
__block UIBackgroundTaskIdentifier bgTask = 0;
bgTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
[[UIApplication sharedApplication] endBackgroundTask:bgTask];
}];
Then, store bgTask somewhere associated with your GET/POST request. Then when your request is complete, do this:
[[UIApplication sharedApplication] endBackgroundTask:bgTask];
Wen app is closed applicationDidEnterBackground was called so put this background task code. put timer or cal the function what ever u want.
- (void)applicationDidEnterBackground:(UIApplication *)application
{
bgTask = [application beginBackgroundTaskWithName:#"MyTask" expirationHandler:^{
[application endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
}];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,
0),
^{
[application endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
});
//// just u call the method want ever u want example
[self notification];
// (or) timer
timer = [NSTimer scheduledTimerWithTimeInterval:1.0
target:self
selector:#selector(notification:)
userInfo:nil
repeats:YES];
}
i think is help ful to u
my app downloads a bunch of data through BLE from a peripheral. If I lock the screen my app gets moved into the background and it starts an background task. The download finishes fine but if the processing (which takes rather long because it is a lot of data) begins the app craches because it cant connect to the database.
I want to stop the execution at that point and wait for the app to become active again, but somehow I cant achieve this. I think I need some kind of semaphore to wait for the app to become active.
Here my code so far:
- (void)viewDidLoad
{
//Some other code
//initialize flag
isInBackgroud = NO;
// check if app is in the background
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(appDidEnterBackground) name:UIApplicationDidEnterBackgroundNotification object:nil];
// check if app is in the foreground
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(appDidEnterForeground) name:UIApplicationDidBecomeActiveNotification object:nil];
}
- (void)appDidEnterBackground {
NSLog(#"appDidEnterBackground");
isInBackground = YES;
UIApplication *app = [UIApplication sharedApplication];
NSLog(#"remaining Time: %f", [app backgroundTimeRemaining]);
bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
NSLog(#"expirationHandler");
[app endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
}];
}
- (void)appDidEnterForeground {
NSLog(#"appDidEnterForeground");
isInBackground = NO;
if (bgTask != UIBackgroundTaskInvalid) {
UIApplication *app = [UIApplication sharedApplication];
[app endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
}
}
//BLE connection and reading data via notification
//when finished [self processData] is called.
- (void)processData {
if (isInBackground) {
//set reminder
UILocalNotification *localNotification = [[UILocalNotification alloc] init];
localNotification.fireDate = [NSDate date];
localNotification.alertBody = [NSString stringWithFormat:#"Data was downloaded, return to the application to proceed processing your data."];
localNotification.timeZone = [NSTimeZone defaultTimeZone];
[[UIApplication sharedApplication] scheduleLocalNotification:localNotification];
UIApplication *app = [UIApplication sharedApplication];
//end background task
[app endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
//wait for application to become active again
while (isInBackground) {
NSLog(#"isInBackground");
NSLog(#"remaining Time: %f", [app backgroundTimeRemaining]);
sleep(1);
}
//process data
}
So I have notices that if I call [app endBackgroundTask:bgTask]; the app just continues running but then crashes when I want connect to my database. Thats why I added the while(isInBackground) loop. I know that this is not good practice because it actively wastes CPU time while doing noting. I should use a semaphore at that point, but I cant figure out how to do it.
Because I'm actively waining in that loop, appDidEnterForegronund is never called and the loop runs forever.
You shouldn't be looping because your app only gets so long to process before it's stopped by iOS. Instead, when your app enters the background, set a state variable that it's in the background. Do the same for the foreground.
Only update the database if you're in the foreground, otherwise, set a state variable that tells your app that you've finished downloading, but still need to process the data. Store the data if you need to.
Then, when your app is relaunched, check the state of that variable and do the processing.
Instead of sitting in a loop waiting for some state to change, set variables, and use event-driven programming.
I am working on the app where video is uploading to Facebook. It is working fine till the app is active and also If it goes to background. For background I used following lines of code to not interrupt the connection.
- (void)applicationDidEnterBackground:(UIApplication *)application
{
UIApplication *app = [UIApplication sharedApplication];
self.backgroundTask = [app beginBackgroundTaskWithExpirationHandler:^{
[app endBackgroundTask:self.backgroundTask];
}];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[[UIApplication sharedApplication] endBackgroundTask:self.backgroundTask];
self.backgroundTask = UIBackgroundTaskInvalid;
});
}
But if user locks the screen by pressing on/off button the NSURLConnection get stops. Is there any other way to make the nsurlconnection active even on the locked screen. Please suggest me some ideas.
Thanks in advance.
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