How to execute long running task in background - objective-c

I want to execute long running task in background in Iphone but code which I using for background task executes only for 3 mins
My code is
- (void)startService{
[self startBackgroundTask];
}
- (void) startBackgroundTask{
self.backgroundDataSendTimer = [NSTimer scheduledTimerWithTimeInterval:backgroundDataSendfrequency
target:self
selector:#selector(dobackgroundDataSendInBackground)
userInfo:nil
repeats:YES];
self.backgroundTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
dispatch_async(dispatch_get_main_queue(), ^
{
if ( self.backgroundTask != UIBackgroundTaskInvalid)
{
[self endBackgroundTask];
self.backgroundTask = UIBackgroundTaskInvalid;
}
});
}];
- (void) endBackgroundTask{
[[UIApplication sharedApplication] endBackgroundTask:self.backgroundTask];
self.backgroundTask = UIBackgroundTaskInvalid;
[self startBackgroundTask];
}

Related

Add continuation block to method

I have a singleton that displays status messages:
+ (FFStatusDisplay*) sharedInstance
{
static FFStatusDisplay* sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken,
^{
sharedInstance = [[FFStatusDisplay alloc] init];
});
return sharedInstance;
}
- (void) showStatus:(NSString*)message Timeout:(float)timeout Controller:(UIViewController*)controller
{
if (![self isDisplaying])
{
[self setIsDisplaying:YES];
[self setStatusAlert:[UIAlertController alertControllerWithTitle:nil message:message preferredStyle:UIAlertControllerStyleAlert]];
[controller presentViewController:[self statusAlert] animated:YES completion:
^{
[NSTimer scheduledTimerWithTimeInterval:timeout target:self selector:#selector(timerExpired:) userInfo:nil repeats:NO];
}];
}
}
- (void)timerExpired:(NSTimer *)timer
{
[[self statusAlert] dismissViewControllerAnimated:YES completion:nil];
[self setStatusAlert:nil];
[self setIsDisplaying:NO];
}
I would like to modify showStatus so that I can optionally call it with a completion block that executes after the message is removed by the timer. I think the declaration should be something like this:
- (void) showStatus:(NSString*)message Timeout:(float)timeout Controller:(UIViewController*)controller withBlock:(nullable id (^)(void))block;
But I don't know how to call the block in the body of the implementation. What I've been trying isn't right:
{
if (![self isDisplaying])
{
[self setIsDisplaying:YES];
[self setStatusAlert:[UIAlertController alertControllerWithTitle:nil message:message preferredStyle:UIAlertControllerStyleAlert]];
[controller presentViewController:[self statusAlert] animated:YES completion:
^{
[NSTimer scheduledTimerWithTimeInterval:timeout target:self selector:#selector(timerExpired:) userInfo:nil repeats:NO];
}];
}
block; //this isn't right
}
Can someone show me how to do this?
Thanks
In Objective-C, you call a block like a C function:
block();
That being said, if you want it to fire after your timer has fired, you'll need to add some mechanism to hang onto the block and call it from your timerExpired: method

Why NSTimer doesn't stop with invalidate?

I make a Cocoa application.I create a NSTimer,it can work,but can't stop with invalidate.The following is my code.Anyone else can help me?
#implementation Document
{
NSTimeInterval elapsedTime;
NSTimer *timer;
}
- (IBAction)start:(id)sender
{
elapsedTime = 0.002f;
dispatch_async(dispatch_get_main_queue(), ^{
if(timer == nil)
{
NSLog(#"Starting");
captureFrame = [[CaptureFrame alloc] init];//captureFrame is another object
timer = [NSTimer scheduledTimerWithTimeInterval:elapsedTime target:captureFrame selector:#selector(sendSingleImageImage) userInfo:nil repeats:YES];
NSThread *thread = [NSThread currentThread];
NSLog(#"Current thread is%#",thread);// I want to see the thread.
}
else
{
NSLog(#"Exist!");
[timer invalidate];
timer = nil;
}
});
[_stopButton setEnabled:YES];
[_startButton setEnabled:NO];
}
- (IBAction)stop:(id)sender
{
dispatch_async(dispatch_get_main_queue(), ^{
[timer invalidate];
timer = nil;
NSThread *sthread = [NSThread currentThread];
NSLog(#"S thread is %#",sthread);// I want to see the thread
});
[_startcastButton setEnabled:YES];
[_stopButton setEnabled:NO];
NSLog(#"Stopping");
}
I tried use the following method,but didn't work.
dispatch_async(dispatch_get_main_queue(), ^(void)block)
I also tried to find whether two actions in the same thread,and the result is yes.
So please tell me how to stop the NSTimer.
Any help is appreciated,thanks a lot in advance.

Run methods in background on certain intervel

(void)applicationWillResignActive:(UIApplication *)application {
NSLog(#"to background");
UIApplication *app = [UIApplication sharedApplication];
// Request permission to run in the background. Provide an
// expiration handler in case the task runs long.
NSAssert(bgTask == UIBackgroundTaskInvalid, nil);
bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
// Synchronize the cleanup call on the main thread in case
// the task actually finishes at around the same time.
dispatch_async(dispatch_get_main_queue(), ^{
if (bgTask != UIBackgroundTaskInvalid)
{
[app 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.
[self performSelector:#selector(hai) withObject:nil afterDelay:30];
NSLog(#"App staus: applicationDidEnterBackground");
dispatch_async(dispatch_get_main_queue(), ^{
if (bgTask != UIBackgroundTaskInvalid)
{
[app endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
}
});
});
}

recurrence with performSelector after delay

-(void)manageNetConnection{
static BOOL closing=FALSE;
NSLog(#"connecting Imap after net");
if([imapStoreObj isStoreConnected] && closing==FALSE){
[imapStoreObj close];
NSLog(#"close store");
closing=TRUE;
[self performSelector:#selector(manageNetConnection) withObject:nil afterDelay:5.0];
return;
}else if ([imapStoreObj isStoreConnected] && closing==TRUE) {
[self performSelector:#selector(manageNetConnection) withObject:nil afterDelay:5.0];
return;
}
closing=FALSE;
[indicatorForGetMail setHidden:NO];
[indicatorForGetMail startAnimation:nil];
netOff=2;
NSLog(#"netOff==%d",netOff);
[editFolderTable setAllowsMultipleSelection:NO];
NSLog(#"connect net");
[self reconnect];
}
This function is expected to call itself until connection is re-established. The problem is that the function doesn't calls itself after the specified delay.
Please help
I encountered the same issue with #performSelector. Use #performSelectorOnMainThread and NSTimer instead.
-(void)manageNetConnection{
...
[self performSelectorOnMainThread:#selector(startTimer:) withObject:#"manageNetConnection" waitUntilDone:YES];
...
}
- (void)startTimer:(NSString *)data{
[NSTimer scheduledTimerWithTimeInterval:5.0 target:self selector:#selector(endTimer:) userInfo:data repeats:NO];
}
- (void)endTimer:(NSTimer *)timer{
NSString *target = [timer userInfo];
SEL targetSelector = NSSelectorFromString(target);
[self performSelectorInBackground:targetSelector withObject:nil];
}
Below line will call -(void)manageNetConnection; method for once after 5.0 if your both conditions are satisfying.
[self performSelector:#selector(manageNetConnection) withObject:nil afterDelay:5.0];
Check for both if condition and else if condition satisfies.
if([imapStoreObj isStoreConnected] && closing==FALSE){
[self performSelector:#selector(manageNetConnection) withObject:nil afterDelay:5.0];
return;
}else if ([imapStoreObj isStoreConnected] && closing==TRUE) {
[self performSelector:#selector(manageNetConnection) withObject:nil afterDelay:5.0];
return;
}
or else you can use which repeats the call
[NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(testMethod:) userInfo:nil repeats:YES];

handling location event with didFinishLaunchingWithOptions in background mode or after app was terminated

I call startMonitoringSignificantLocationChanges in location manager init.
I expect that if there is a location event didFinishLaunchingWithOptions will be launched and execute the following code. (including background mode and if app was terminated)
The app freezes (black screen when i try to launch it after a few hours in the background in different location)
Probably i'm doing something wrong when i get a location event. I would appreciate if someone has an idea what's the problem.
By the way, there is no way to simulate this kind of location event but physically move to a different location with different cell tower. is there? ... :(
LocationController.m :
- (id)init
{
self = [super init];
if (self != nil) {
self.locationManager = [[[CLLocationManager alloc] init] autorelease];
self.locationManager.delegate = self;
self.locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters;
[self.locationManager startUpdatingLocation];
[self.locationManager startMonitoringSignificantLocationChanges];
[NSTimer scheduledTimerWithTimeInterval:10 target:self selector:#selector(stop) userInfo:nil repeats:NO];
}
return self;
}
appdelegate.m :
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
id locationValue = [launchOptions objectForKey:UIApplicationLaunchOptionsLocationKey];
if (locationValue)
{
[[LocationController sharedInstance] startMonitoringSignificantLocationChanges];
UIApplication *app = [UIApplication sharedApplication];
bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
[app endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
}];
[self updateLocationService];
return YES;
}
}
- (void) updateLocationService {
[[LocationController sharedInstance] start];
[NSTimer scheduledTimerWithTimeInterval:2 target:self selector:#selector(stop) userInfo:nil repeats:NO];
}
- (void) stop {
[[LocationController sharedInstance] stop];
}
Thank you!
Your didFinishLaunchingWithOptions: method does not return anything if there is no UIApplicationLaunchOptionsLocationKey set.
Just move the return statement outside your if clause:
if (locationValue)
{
...
}
return YES;