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
Related
I have in my app login and logout process here I am facing so many problem with background timer services process. Below code I am maintaining for notification and JSON service background while applicationDidBecomeActive.
-(void)applicationDidBecomeActive:(UIApplication *)application
{
checkJSONtimer = [NSTimer scheduledTimerWithTimeInterval:300 target:self selector:#selector(updateJSON) userInfo:nil repeats:TRUE];
NSLog(#"%s", __PRETTY_FUNCTION__);
application.applicationIconBadgeNumber = 0;
}
Problems below
Before login, timer activity started
After logout, timer activity not stopping
If I commented above timer after background to come active state,
then timer not working
Every time without logout , cleared background app running again
open I can go from login
FYI : Above timer selector method I am maintaining below three places
1. application didFinishLaunchingWithOptions
2. applicationDidBecomeActive
3. applicationDidEnterBackground
How to solve above problems. Please help me!
Before login, timer activity started
For that you have to make a check whether the user is log-in the app or not.For example
-(void)applicationDidBecomeActive:(UIApplication *)application{
if(isUserLogedIn) {
checkJSONtimer = [NSTimer scheduledTimerWithTimeInterval:300 target:self selector:#selector(updateJSON) userInfo:nil repeats:TRUE];
NSLog(#"%s", __PRETTY_FUNCTION__);
application.applicationIconBadgeNumber = 0;
}
}
After logout, timer activity not stopping
For that, make a Notification which will trigger when the user log-out from the app.When the notification triggers the invalidate the timer.[myTimer inValidate].
If I commented above timer after background to come active state, then timer not working
No need to comment the timer.Just manage the timer affectively.
Every time without logout , cleared background app running again open I can go from login.
For that you have to manage the User session in NSUserDefaults.Check the value, if the value is not null, the log-in user automatically.
If you want to cancel the timer you can use something like this:
AppDelegate* appDelegate = (AppDelegate *)[UIApplication sharedApplication].delegate;
[appDelegate.checkJSONtimer invalidate]
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.
I'am working on an application in xcode-5, I need to upload some data in background mode, I need a set of code to be repeated till the device terminates the background process. I am able to do it once but I need to repeat again and again so that i can check the connectivity and perform the upload. I doing the same thing when the application comes in active mode.
Currently I'm using Timers with the following code :
- (void)applicationDidEnterBackground:(UIApplication *)application
{
[bgrdTimer_ActiveMode invalidate];
bgrdTimer_ActiveMode=nil;
self.backgroundTask=[application beginBackgroundTaskWithExpirationHandler:^{
self.backgroundTask=UIBackgroundTaskInvalid;
}];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0),^{
bgrdTimer_BackGroundMode=[NSTimer scheduledTimerWithTimeInterval:40.00 target:self selector:#selector(repeatUploadProcess_BackGroundMode) userInfo:nil repeats:YES];
});
}
- (void)applicationDidBecomeActive:(UIApplication *)application
{
[bgrdTimer_BackGroundMode invalidate];
bgrdTimer_BackGroundMode=nil;
bgrdTimer_ActiveMode=[NSTimer scheduledTimerWithTimeInterval:40.00 target:self selector:#selector(repeatUploadProcess_ActiveMode) userInfo:nil repeats:YES];
}
repeatUploadProcess_ActiveMode and repeatUploadProcess_BackGroundMode are the methods containing the set of code which is suppose to be repeated
the problem is when I invalidate the bgrdTimer_ActiveMode the other timer dosn't gets invoked.
Have you enabled the background fetch mode? Click on the project target, and under the capabilities tab select "background fetch".
Theoretically you should also be setting [application endBackgroundTask:bgTask]. It sounds like you know, but if it never ends, iOS will end up killing your app.
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.
My application after 30 seconds of doing nothing should came to the background. If there's no activity after 30 seconds, I want to log the user out. It's application which contains user interface. When the user want to back he must write again his username and password. I put below my code:
Timer.m:
#define kApplicationTimeoutInMinutes 0.1
#define kApplicationDidTimeoutNotification #"AppTimeOut"
#interface Timer : UIApplication
{
NSTimer *myidleTimer;
}
-(void)resetIdleTimer;
Timer.h:
#implementation Timer
//here we are listening for any touch. If the screen receives touch, the timer is reset
-(void)sendEvent:(UIEvent *)event
{
[super sendEvent:event];
if (!myidleTimer)
{
[self resetIdleTimer];
}
NSSet *allTouches = [event allTouches];
if ([allTouches count] > 0)
{
UITouchPhase phase = ((UITouch *)[allTouches anyObject]).phase;
if (phase == UITouchPhaseBegan)
{
[self resetIdleTimer];
}
}
}
//as labeled...reset the timer
-(void)resetIdleTimer
{
if (myidleTimer)
{
[myidleTimer invalidate];
}
//convert the wait period into minutes rather than seconds
int timeout = kApplicationTimeoutInMinutes * 60;
myidleTimer = [NSTimer scheduledTimerWithTimeInterval:timeout target:self selector:#selector(idleTimerExceeded) userInfo:nil repeats:NO];
}
//if the timer reaches the limit as defined in kApplicationTimeoutInMinutes, post this notification
-(void)idleTimerExceeded
{
[[NSNotificationCenter defaultCenter] postNotificationName:kApplicationDidTimeoutNotification object:nil];
}
AppDelegate.m:
#implementation AppDelegate
-(BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(applicationDidTimeout:) name:kApplicationDidTimeoutNotification object:nil];
return YES;
}
-(void)applicationDidTimeout:(NSNotification *) notif
{
NSLog (#"time exceeded!!");
//This is where storyboarding vs xib files comes in. Whichever view controller you want to revert back to, on your storyboard, make sure it is given the identifier that matches the following code. In my case, "mainView". My storyboard file is called MainStoryboard.storyboard, so make sure your file name matches the storyboardWithName property.
UIViewController *viewController = [[UIStoryboard storyboardWithName:#"Main" bundle:NULL] instantiateViewControllerWithIdentifier:#"login"];
[(UINavigationController *)self.window.rootViewController pushViewController:viewController animated:YES];
}
//metoda, która informuje o przejsciu z aktywnego do nieaktywnego stanu
- (void)applicationWillResignActive:(UIApplication *)application
{
// Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
// Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
}
//- (UIBackgroundTaskIdentifier)beginBackgroundTaskWithExpirationHandler:(void (^)(void))handler
- (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.
}
- (void)applicationWillEnterForeground:(UIApplication *)application
{
// Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
}
- (void)applicationDidBecomeActive:(UIApplication *)application
{
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
}
- (void)applicationWillTerminate:(UIApplication *)application
{
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
}
If I understand this correctly, you want a similar functionality to some password managers, which have a functionality of locking themselves after a certain period of time.
First, lets make clear that you cannot send the app to background on iOS. That is up to the user.
What you can do is lock the application after a certain period of time and display user and password prompt screen. To do this you need a timer (NSTimer), which gets restarted at every action by the user. If at any time timer gets to it's end - the 30 second interval passes, timer will execute your method, where you can display a modal view controller with user and password prompt. This way the app will stay locked until user enters username and password.
Detecting last action can also be done in multiple ways:
Detecting last user's touch
Adding few lines of code to all app actions
Swizzling navigation methods
...