NSTimer and closing app while in background - ios

I've encountered problem with closing the app while in background.
When working in the background, tapping 2x and swiping the app to close it, the app doesn't call the applicationWillTerminate:(UIApplication *)application, but goes to #autoreleasespool, which is definitely crash.
I suppose it is connected with working NSTimer, because without initializing it the app closes properly from background.
I wanted to disable the NSTimer in the appDelegate with no success, since NSTimer can be disabled only from the same thread.
I am starting NSTtimer in init of my class:
[NSTimer scheduledTimerWithTimeInterval:5.0f target:self selector:#selector(checkLastSeenTimeofPeripheral) userInfo:nil repeats:YES];
I wanted to stop it while going to background using the answer given here, but still it doesn't seem to stop the timer and the app crashes on termination.
EDIT:
Initializing in myClass
- (id)init {
self = [super init];
if(self){
//check the connection timer
[self startCheckLastSeenTimeOfPeripheralTimer];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(stopCheckLastSeenTimeOfPeripheralTimer) name:UIApplicationDidEnterBackgroundNotification object:[UIApplication sharedApplication]];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(startCheckLastSeenTimeOfPeripheralTimer) name:UIApplicationWillEnterForegroundNotification object:[UIApplication sharedApplication]];
methods to start/stop timer
-(void)startCheckLastSeenTimeOfPeripheralTimer {
_checkLastSeenTimeOfPeripheralTimer = [NSTimer scheduledTimerWithTimeInterval:5.0f
target:self
selector:#selector(checkLastSeenTimeofPeripheral)
userInfo:nil
repeats:YES];
NSLog(#"checkLastSeenTimeofPeripheralTimer started");
}
-(void)stopCheckLastSeenTimeOfPeripheralTimer {
if (_checkLastSeenTimeOfPeripheralTimer) {
[_checkLastSeenTimeOfPeripheralTimer invalidate];
_checkLastSeenTimeOfPeripheralTimer = nil;
NSLog(#"checkLastSeenTimeofPeripheralTimer stopped");
}
else {
NSLog(#"checkLastSeenTimeofPeripheralTimer not initialized - can't stop");
}
}

According to documentation appWillTerminate is not being called when closing suspended App: Suspended apps receive no notification when they are terminated; the system kills the process and reclaims the corresponding memory.
Apps get suspended by the system while in background without informing about it.

Related

Wrong handling when resuming from MFMailComposeViewController

My application is a quizz game. The user has a limited time to answer the question.
A timer is used for that. When the time runs out, a simple sound is triggered.
NSTimer *m_timer;
In function viewDidAppear:
m_timer = [NSTimer scheduledTimerWithTimeInterval:1.0f target:self selector:#selector(decrementSpin) userInfo:nil repeats:YES];
In my fisrt version, I encountered the following situation:
If during a question, an incoming call interrupts the game, the timer was still counting during the call.
I fixed this problem by adding in function viewDidLoad:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(appDidEnterInBackground:) name:UIApplicationDidEnterBackgroundNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(appWillEnterForeground:) name:UIApplicationWillEnterForegroundNotification object:nil];
- (void)appDidEnterInBackground:(NSNotification *)notification {
[[SoundManager sharedManager]stopMusic:NO];
[m_timer invalidate];
m_timer = nil;
}
- (void)appWillEnterForeground:(NSNotification *)notification {
if(m_timer) {
[m_timer invalidate];
m_timer = nil;
}
//NSLog(#"%d", self.TimerbackgroundView.percent);
m_timer = [NSTimer scheduledTimerWithTimeInterval:1.0f target:self selector:#selector(decrementSpin) userInfo:nil repeats:YES];
}
The function decrementSpin updates the clock image and plays the sound if the player has run out of time.
Everything works well.
Since my last version, I added a feature. The user can report a question (for incorrect content) by pressing a button.
When a button is pressed it opens the mail app with a prefilled content.
MFMailComposeViewController*mailComposerVC = [[MFMailComposeViewController alloc] init];
if ([MFMailComposeViewController canSendMail]) {
mailComposerVC.mailComposeDelegate = self;
[mailComposerVC setToRecipients:#emailAddress];
[mailComposerVC setSubject:emailSubject];
[mailComposerVC setMessageBody:emailBody isHTML:NO];
[self presentViewController:mailComposerVC animated:YES completion:^{
}];
}
else{
NSLog(#"Unable to send message");
}
- (void)mailComposeController:(MFMailComposeViewController *)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError *)error{
[controller dismissViewControllerAnimated:YES completion:^{
}];
}
It seems that my application doesn't handle correctly when the user sends the mail and returns to the Question page.
The timer doesn't pause (like the incoming call case), and in addition, when the Question page re-appears the clock-image shows the initial image (exactly like when the page appears for the first time)
This bug causes the sound (the one triggered when the user runs out of time) to be played during the next Page.
The only thing that comes in my mind that the events notified by UIApplicationDidEnterBackgroundNotification and UIApplicationWillEnterForegroundNotification are not covered in the case of MFMailComposeViewController.
Any idea ?
You are correct. The app is still running the in the foreground.
So a possible solution would be to execute the same code of appDidEnterInBackground when opening the Mail.app, and to do the same as appWillEnterForeground in the body of
- (void)mailComposeController:(MFMailComposeViewController *)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError *)error{
[controller dismissViewControllerAnimated:YES completion:^{
}];
}
What do you think ?

listen server in applicationDidEnterBackground

I have a messaging application and I want that the application listens the server on background and when new message comes, fires notification.
I tried to do that with timers and backgroundtasks, i can listen the server and the application fires the notification but when i reopen the application, i can not interact with anything, like the application is locked. Can you suggest any proper way to do that?
- (void)applicationDidEnterBackground:(UIApplication *)application
{
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(sbCheckServerRecords) name:#"UIApplicationDidEnterBackgroundNotification" object:nil];
}
-(void) sbCheckServerRecords{
self.tmrCheckRecords = [[NSTimer alloc] initWithFireDate:[NSDate dateWithTimeIntervalSinceNow:1] interval:10.0 target:self selector:#selector(sbCheckRecords) userInfo:nil repeats:YES];
[self.tmrCheckRecords fire];
}
-(void) sbCheckRecords{
#try{
if(any message comes)
notify user;
}
}
I have used timer as dispatch_source_t and
this class worked for me:
https://gist.github.com/maicki/7622108

How change UIImageView in external screen on background mode

I need show changes in external screen with UIImageView when my ios app is on background mode.
I use this code to change the UIImageView
campaingTimer = [NSTimer scheduledTimerWithTimeInterval:timeFirstAd target:self selector:#selector(changeImage) userInfo:nil repeats:NO];
This works when my app is active, but when in background, enters the changeImage method, but not change the picture.
NSTimer selectors are not guaranteed to fire off in the background. Unless you're registering for specific permissions, such as playing music in the background, and whatever you're actually doing in the background is directly related to the permission you asked for, you should work under the assumption you will not be able to execute code while the app is backgrounded, as that'll set you up to succeed much better than trying to find workarounds.
In this scenario, it seems like you want to change the image after so much time passes. That NSTimer you have (assuming your methods are written correctly) will work while the app is in the foreground, but to deal with background I recommend listening for the appDidEnterBackground and appWillEnterForeground and posting notifications (see sample code below).
AppDelegate.m
================
- (void)applicationDidEnterBackground:(UIApplication *)application
{
self.currentTime = [NSDate date];
}
- (void)applicationWillEnterForeground:(UIApplication *)application
{
[[NSNotificationCenter defaultCenter] postNotificationName:kNotificationNameForBecameActive object:nil userInfo:#{kUserInfoForBecameActive: self.currentTime}];
}
================
ViewController.m
================
- (void)viewDidLoad
{
[super viewDidLoad];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(didBecomeActive:) name:kNotificationNameForBecameActive object:nil];
}
- (void)didBecomeActive:(NSNotification *)notification
{
NSDate *sleepDate = notification.userInfo[kUserInfoForBecameActive];
NSTimeInterval secondsPassed = [[NSDate date] timeIntervalSinceDate:sleepDate];
if (secondsPassed >= timeFirstAd)
{
[self changeImage];
}
// reinitialize NSTimer
}
================
Alternatively, you could post notifications for both appDidEnterBackground and appWillEnterForeground and save the time there, along with invalidating your NSTimer and restarting it.

How does an iOS app know about an incoming call and number that is calling while running in background? [duplicate]

This question already has an answer here:
Detecting the call events in ios
(1 answer)
Closed 8 years ago.
I want my iOS app to do something special when a call comes in and the app itself is running in background. I Googled and found a function named applicationWillResignActive, but it is called when an application is running and is interrupted. This interruption could be due to an incoming call or an sms or when the user quits the application. But in my case I want my app to be in the background waiting for an incoming call and I would also like the app to read the number that called and look up that number in the contacts list of the phone.
As suggested by other people that there was another similar question at this link but that says that Apple would reject the App as a breach of privacy. But in my case I am not extracting the numbers who called. The number will be used only while the call is coming after call is either received or cancelled I would clear the variable storing that value.
Please let me know how this can be achieved and if doing this would be fine with Apple.
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(appHasGoneInBackground) name:UIApplicationDidEnterBackgroundNotification
object:nil];
Check for application enter in background....
-(void)appHasGoneInBackground
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
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
t = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector(updateCounter) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:t forMode:NSDefaultRunLoopMode];
[[NSRunLoop currentRunLoop] run];
});
}
-(void)updateCounter
{ [[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(call) name:AVAudioSessionInterruptionNotification object:nil];
}

Are performSelector afterDelay calls reset when applicationDidEnterBackground?

I totally understand if they are, but what I'm looking for is a timer that pauses when the application enters the background and unpauses after the user returns to the app. I do not need a background task; I just want to make sure that after approximately x minutes within the app a certain action occurs whether that is today or tomorrow.
Thanks!
Brett
Backgrounding the app (assuming you have no background task) doesn't "pause" the timer. It's still counting down in theory so if the app is reopened, it will fire if enough time has passed. This goes for NSTimer's as well. (Let me know if you want more details as to why and I'll edit the answer).
Consider using the following code:
#implementation MyCustomClass {
int elapsedTime;
NSTimer *timer;
}
- (id) init {
if ( ( self = [super init] ) ) {
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(applicationEnteredBackground)
name:UIApplicationDidEnterBackgroundNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(applicationEnteredForeground)
name:UIApplicationDidBecomeActiveNotification
object:nil];
}
return self;
}
- (void) applicationEnteredForeground {
timer = [NSTimer timerWithTimeInterval:1
target:self
selector:#selector(timerTicked)
userInfo:nil
repeats:YES];
[[NSRunLoop mainRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
}
- (void) applicationEnteredBackground {
[timer invalidate];
}
- (void) timerTicked {
elapsedTime += 1;
// If enough time passed, do something
}

Resources