How to make a call from background thread in iOS - ios

I call a method from background thread as follows.
- (void)applicationDidEnterBackground:(UIApplication *)application
{
UIApplication *app = [UIApplication sharedApplication];
//create new uiBackgroundTask
__block UIBackgroundTaskIdentifier bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
[app endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
}];
//and create new timer with async call:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
//run function methodRunAfterBackground
NSTimer* t = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector(methodRunAfterBackground) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:t forMode:NSDefaultRunLoopMode];
[[NSRunLoop currentRunLoop] run];
});
}
After 15 seconds, I have to make a call to a number. I use [[UIApplication sharedApplication] openURL:[NSURL URLWithString:[NSString stringWithFormat:#"tel://%#",_number]]];but unable to make a call while application is in background state. Can we make a call from background state? If yes, how?

Related

UIBackgroundMode location works only for three minutes on iOS 7

I have implemented following in my applicationDidEnterBackground.
It will work for three minutes in background and after three minutes beginBackgroundTaskWithExpirationHandler will run, How can i make it work for longer?
- (void)applicationDidEnterBackground:(UIApplication *)application
{
bgTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
NSLog(#"ending background task");
[[UIApplication sharedApplication] endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
}];
NSTimer *timerlocation = [NSTimer scheduledTimerWithTimeInterval:30.0
target:locationManager
selector:#selector(startUpdatingLocation)
userInfo:nil
repeats:YES];
}
Make sure to use the background mode for "receiving location updates." Then if you call startUpdatingLocation while the app is in the foreground, it will continue to check the location in the background.

NSTimer with UITableView

I have UIView with button Start, UILabel with my NSTimer time and UITableView with time record.
Problem is when I scroll my tableview - NSTimer freeze, why I can do to they work async?
My code:
- (IBAction)startTimer {
_startButton.hidden = YES;
_stopButton.hidden = NO;
isRun = YES;
UIBackgroundTaskIdentifier bgTask = UIBackgroundTaskInvalid;
UIApplication *app = [UIApplication sharedApplication];
bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
[app endBackgroundTask:bgTask];
}];
myTimer = [NSTimer scheduledTimerWithTimeInterval:0.01f
target:self
selector:#selector(tick)
userInfo:nil
repeats:YES];
}
- (void)tick {
_timeSot++;
sot++;
/*some code*/
myTime = [NSString stringWithFormat:#"%#:%#:%#", minStr, secStr, sotStr];
[_timeLabel setText:myTime];
}
Add myTimer to NSRunLoopCommonModes:
myTimer = [NSTimer scheduledTimerWithTimeInterval:0.01f
target:self
selector:#selector(tick)
userInfo:nil
repeats:YES];
[[NSRunLoop mainRunLoop] addTimer:myTimer forMode:NSRunLoopCommonModes];

NSTimer lag fire after background

timer = [NSTimer bk_timerWithTimeInterval:60 block:^(NSTimer *timer) { ....} repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
+ (id)bk_timerWithTimeInterval:(NSTimeInterval)inTimeInterval block:(void (^)(NSTimer *timer))block repeats:(BOOL)inRepeats
{
NSParameterAssert(block != nil);
return [self timerWithTimeInterval:inTimeInterval target:self selector:#selector(bk_executeBlockFromTimer:) userInfo:[block copy] repeats:inRepeats];
}
+ (void)bk_executeBlockFromTimer:(NSTimer *)aTimer {
void (^block)(NSTimer *) = [aTimer userInfo];
if (block) block(aTimer);
}
If I fold and unfold application after a few minutes, almost always the timer is firing immediately and sometimes it fire with delay.
Why the timer does not always work immediately?
try to add this in "didFinishLaunchingWithOptions" in your Appdelegate
__block UIBackgroundTaskIdentifier locationUpdater =[[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
[[UIApplication sharedApplication] endBackgroundTask:locationUpdater];
locationUpdater=UIBackgroundTaskInvalid;
} ];

How to call a method every nth minute while app is in background?

Hello everyone I need to call a method when the app is in background. This is now i am trying to achieve this but the timer is never called.
- (void)applicationDidEnterBackground:(UIApplication *)application
{
UIApplication* app = [UIApplication sharedApplication];
bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
[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.timer = nil;
[self initTimer];
});
}
- (void)initTimer {
DebugLog(#"timer INIT!!!!!");
if (self.timer == nil) {
self.timer = [NSTimer scheduledTimerWithTimeInterval:0.3
target:self
selector:#selector(checkUpdates:)
userInfo:nil
repeats:YES];
}
}
- (void)checkUpdates:(NSTimer *)timer{
[UIApplication sharedApplication].applicationIconBadgeNumber++;
DebugLog(#"timer FIRED!!!!!");
[self initTimer];
}
I need to call the method checkUpdates every Nth minute or seconds while the app is in background.Please note that initTimer is called when the app enters background but the timer is never called.
Apple will not allow the use the application in background if you want to use the application in background you have to use the 'UIBackgroundModes'.
and after that from Appdeligate you can manage this thread
you should check
http://developer.apple.com/library/ios/#documentation/General/Reference/InfoPlistKeyReference/Articles/iPhoneOSKeys.html
hope this will help you

Getting user location every n minutes after app goes to background

I am trying to implement the suggestions given in this post.
Unfortunately the steps are not clear to me. I tried implementing those suggestions, but the backgroundTimeRemaining continues to decrease even after I start and stop locationServices. This is how I developed it:
- (void)applicationDidEnterBackground:(UIApplication *)application {
UIApplication* app = [UIApplication sharedApplication];
bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
[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.timer = nil;
[self initTimer];
});
}
initTimer:
- (void)initTimer {
// Create the location manager if this object does not
// already have one.
if (nil == self.locationManager)
self.locationManager = [[CLLocationManager alloc] init];
self.locationManager.delegate = self;
[self.locationManager startMonitoringSignificantLocationChanges];
if (self.timer == nil) {
self.timer = [NSTimer scheduledTimerWithTimeInterval:0.3
target:self
selector:#selector(checkUpdates:)
userInfo:nil
repeats:YES];
}
}
checkUpdates:
- (void)checkUpdates:(NSTimer *)timer{
UIApplication* app = [UIApplication sharedApplication];
double remaining = app.backgroundTimeRemaining;
if(remaining < 580.0) {
[self.locationManager startUpdatingLocation];
[self.locationManager stopUpdatingLocation];
[self.locationManager startMonitoringSignificantLocationChanges];
}
DbgLog(#"Reminaing %f", app.backgroundTimeRemaining);
}
Does anyone have a suggestion on what might be wrong in my code? Both initTimer and checkUpdates are being called, but only during for the background execution time (+- 10 Mins). I want the app to update the location every n minutes "forever".
My app's UIBackgroundModes is set to location.
UPDATE:
I am now resetting the timer on didUpdateToLocation and didFailWithError. But still the backgroundTimeRemaining keeps decreasing:
- (void)locationManager:(CLLocationManager *)manager
didUpdateToLocation:(CLLocation *)newLocation
fromLocation:(CLLocation *)oldLocation {
NSLog(#"Did Update Location = %f / %f", [newLocation coordinate].latitude, [newLocation coordinate].longitude);
UIApplication* app = [UIApplication sharedApplication];
bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
[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 initTimer];
});
}
- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error {
[self.locationManager stopUpdatingLocation];
UIApplication* app = [UIApplication sharedApplication];
bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
[app endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
}];
// Start the long-running task and return immediately.
[self initTimer];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// Do the work associated with the task.
});
}
I am also invalidating the timer:
- (void)checkUpdates:(NSTimer *)timer{
UIApplication* app = [UIApplication sharedApplication];
double remaining = app.backgroundTimeRemaining;
if(remaining < 580.0 && remaining > 570.0) {
[self.timer invalidate];
self.timer = nil;
[self.locationManager startUpdatingLocation];
[self.locationManager stopUpdatingLocation];
}
DbgLog(#"*************************Checking for updates!!!!!!!!!!! Reminaing %f", app.backgroundTimeRemaining);
}
To anyone else having a nightmare of a time trying to figure this one out, I have a simple solution.
Study the example from raywenderlich.com. The sample code works perfectly, but unfortunately there's no timer during background location. This will run indefinitely.
Add timer by using this code snippet:
-(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];
}
Just don't forget to add "App registers for location updates" in info.plist.
After some days trying all possible solutions I was finally able to make it work. Here is what was wrong in my solution:
I need to setup 2 UIBackgroundTaskIdentifiers, one for the timer, and another one for the locationManager
On my solution, just add:
UIApplication *app = [UIApplication sharedApplication];
bgTask2 = [app beginBackgroundTaskWithExpirationHandler:^{
[app endBackgroundTask:bgTask2];
bgTask2 = UIBackgroundTaskInvalid; }];
self.locationManager.delegate = self;
[self.locationManager startUpdatingLocation];
Once you're in the background you can only have 10 minutes of additional time. There is no way to extend that. You should use the location background services instead.
Working Code(Entire Stepwise Code)
Modified scurioni's code
Step 1
Go to project -> Capabilities -> Background Modes -> select Location updates.
Go to Project -> Info -> add a key NSLocationAlwaysUsageDescription with an optional string.
Step 2
Add this code to AppDelegate.m
#interface AppDelegate ()<CLLocationManagerDelegate>
#property (strong, nonatomic) CLLocationManager *locationManager;
#property (strong, nonatomic) NSTimer *timer;
#end
Step 3
Add This Code in to applicationDidEnterBackground method in AppDelegate.m
- (void)applicationDidEnterBackground:(UIApplication *)application {
UIApplication *app = [UIApplication sharedApplication];
__block UIBackgroundTaskIdentifier bgTaskId =
[app beginBackgroundTaskWithExpirationHandler:^{
[app endBackgroundTask:bgTaskId];
bgTaskId = UIBackgroundTaskInvalid;
}];
dispatch_async( dispatch_get_main_queue(), ^{
self.timer = nil;
[self initTimer];
[app endBackgroundTask:bgTaskId];
bgTaskId = UIBackgroundTaskInvalid;
});
}
- (void)initTimer {
if (nil == self.locationManager)
self.locationManager = [[CLLocationManager alloc] init];
self.locationManager.delegate = self;
[self.locationManager requestAlwaysAuthorization];
[self.locationManager startMonitoringSignificantLocationChanges];
if (self.timer == nil) {
self.timer = [NSTimer scheduledTimerWithTimeInterval:0.3
target:self
selector:#selector(checkUpdates:)
userInfo:nil
repeats:YES];
}
}
- (void)checkUpdates:(NSTimer *)timer{
UIApplication *app = [UIApplication sharedApplication];
double remaining = app.backgroundTimeRemaining;
if(remaining < 580.0) {
[self.locationManager startUpdatingLocation];
[self.locationManager stopUpdatingLocation];
[self.locationManager startMonitoringSignificantLocationChanges];
}
}
- (void)locationManager:(CLLocationManager *)manager
didUpdateToLocation:(CLLocation *)newLocation
fromLocation:(CLLocation *)oldLocation {
NSLog(#"Did Update Location = %f / %f", [newLocation coordinate].latitude, [newLocation coordinate].longitude);
[self updateLocationWithLatitude:[newLocation coordinate].latitude andLongitude:[newLocation coordinate].longitude];
UIApplication* app = [UIApplication sharedApplication];
__block UIBackgroundTaskIdentifier bgTask =
bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
[app endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
}];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[self initTimer];
});
}
- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error {
[self.locationManager stopUpdatingLocation];
UIApplication *app = [UIApplication sharedApplication];
__block UIBackgroundTaskIdentifier bgTask =
bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
[app endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
}];
[self initTimer];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// Do the work associated with the task
});
}
-(void)updateLocationWithLatitude:(CLLocationDegrees)latitude
andLongitude:(CLLocationDegrees)longitude{
//Here you can update your web service or back end with new latitude and longitude
}

Resources