If that isn't possible, then how may I do it after, say, 3 minutes of app usage? This is going to be used for a Rate Us alert but I would rather the user have some time to actually use the app before it asks for them to rate.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)options {
// ...
if ([self plusPlusLaunchCount] == 2) {
[self showRateUsAlert];
}
return YES;
}
- (void)showRateUsAlert {
// show the Rate Us alert view
}
- (NSInteger)plusPlusLaunchCount {
static NSString *Key = #"launchCount";
NSInteger count = 1 + [[NSUserDefaults standardUserDefaults] integerForKey:Key];
[[NSUserDefaults standardUserDefaults] setInteger:count forKey:Key];
return count;
}
Instead of making a "Rate Us" alert yourself, why don't you use third-party libraries? This kind of thing has been done so many times anyway.
This is one of a really good one : iRate
Not exactly answer to your question in the title.
You need to set an NSTimer for the time interval you want to show the alert. When the application launches start the timer and after the interval you set finishes, display the alert.
I would suggest you use DidBecomeActive which is called every time you launch app, and come from background/sleep mode:
You would need to cancel the timer in case user doesn't use app for so long.
- (void)applicationDidBecomeActive:(UIApplication *)application{
// Override point for customization after application launch.
rateUsTimer = [[NSTimer scheduledTimerWithTimeInterval:180
target:self
selector:#selector(showRateUsAlert)
userInfo:nil
repeats:NO] retain];
}
- (void)applicationWillResignActive:(UIApplication *)application{
[rateUsTimer_ invalidate];
[rateUsTimer_ release];
rateUsTimer = nil;
}
- (void)applicationDidEnterBackground:(UIApplication *)application{
[rateUsTimer_ invalidate];
[rateUsTimer_ release];
rateUsTimer = nil;
}
- (void)showRateUsAlert {
//Here you present alert
[rateUsTimer_ release];
rateUsTimer = nil;
}
Related
I want to maintain timer with multiple UIViewControllers.
Like, I am creating one timer in ViewController1 for 60 second interval.
after 20 second, application navigates to ViewController2.(so 40 second is remaining to execute timer).
After 20 second stay i come back to ViewController1, at this time timer should execute after 40 second of come back and then after it will execute after 60 second.
So how can I do this?
Thanks in advance.
If you want to use one item across several instances, try it with the singleton design pattern. But like it seems, you never navigate back from your VC1, so all obejects are still there.
On the base of How to Pause/Play NSTimer? you can change some parts to make it fitting your case.
- (void)stopTimer
{
if( timer )
{
[timer invalidate];
[timer release]; // if not ARC
timer = nil;
}
}
- (void)startTimer
{
[self stopTimer];
int interval = 1.;
timer = [[NSTimer scheduledTimerWithTimeInterval:interval
target:self
selector:#selector(countUp)
userInfo:nil repeats:YES] retain]; // retain if not ARC
}
-(void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[self timerStart];
}
-(void)viewWillDisappear:(BOOL)animated
{
[self stopTimer];
[super viewWillDisappear:animated];
}
-(void)countUp
{
if(timePassed++ >= 60) // defined in header
{
timePassed=0;
[self myCustomEvent];
}
}
Although it may be a solution with better practices, you can set the NSTimer inside your AppDelegate and make the AppDelegate manage segues to push or pop your UIViewControllers
I want to do something like this in my iOS app. Let's say user open the app now. then I want to show a view. during that 1st hour, no matter how manytimes he open the app I need to show the 1st view through out this hour. when start a new hour I want to show the view 2. again after the 3rd hour I need to show that first view.
Like wise
1hr - view 1
2hr - view 2
3hr - view 1
4hr - view 2
How can I monitor this hours changing from my ios app even its not runing in the background
Thank you
If the app isn't running in the background, you really can't monitor the hour changing. But that doesn't matter since you can't show a view when the app is not running.
When the app is running, just use NSTimer and set it to repeat at the right time to tell your controller that the hour is changing. Let the controller deal with figuring out which view to show.
This is important, because you have to make the decision of which view to show even when the hour isn't changing. For example, when you first open the app.
See NSTimer
AppDelegate.m
- (void)applicationDidEnterBackground:(UIApplication *)application {
NSUserDefaults *standardDefaults = [NSUserDefaults standardUserDefaults];
[standardDefaults setObject:[NSDate date] forKey:#"kTimeInterval"];
}
- (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 {
NSUserDefaults *standardDefaults = [NSUserDefaults standardUserDefaults];
NSDate *previousDate = [standardDefaults objectForKey:#"kTimeInterval"];
NSTimeInterval secondsPassed = [[NSDate date] timeIntervalSinceDate:previousDate];
if (secondsPassed >= CHECK_SWAP_VIEW) {
// Change your view here.
}
}
Here you can do like save time when application goes in background or quit. Then whenever application will open check for previous time and then swap your views.
I've seen hundreds of solutions of how to get a NSTimer to run in the background.
I know that it is possible, just look at apps like Strava and Runkepper that tracks your time when working out.
But what is the best practice solution for doing so? I can't find one unison solution for this.
Also, I would like the NSTimer to be used across different UIViewControllers. How is this done as a best practice?
Thanks in regards! :)
NSTimers don't run in the background. Store the current time and the elapsed time of the timer when you got the background. When you come back to the foreground, you set up a new timer, using those two pieces of information to setup any state or data that needs to reflect the total elapsed time.
To share between viewCOntroller, just have one object implement this timer, and expose a property on it (e.g. elapsedTime) that gets updated every time interval . Then you can have the viewCOntrollers (that have a reference to that object) observe that property for changes.
You Can Try This Code in Your application NSTimers don't run in the background. acceding to apple But We Try forcefully Only 3 mint
AppDelegate.h
#property (nonatomic, unsafe_unretained) UIBackgroundTaskIdentifier backgroundTaskIdentifier;
#property (nonatomic, strong) NSTimer *myTimer;
- (BOOL) isMultitaskingSupported;
- (void) timerMethod:(NSTimer *)paramSender;
AppDelegate.m
- (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.
if ([self isMultitaskingSupported] == NO)
{
return;
}
self.myTimer =[NSTimer scheduledTimerWithTimeInterval:1.0f target:self selector:#selector(timerMethod:) userInfo:nil
repeats:YES];
self.backgroundTaskIdentifier =[application beginBackgroundTaskWithExpirationHandler:^(void) {
[self endBackgroundTask];
}];
}
pragma mark - NSTimer Process
- (BOOL) isMultitaskingSupported
{
BOOL result = NO;
if ([[UIDevice currentDevice]
respondsToSelector:#selector(isMultitaskingSupported)]){ result = [[UIDevice currentDevice] isMultitaskingSupported];
}
return result;
}
- (void) timerMethod:(NSTimer *)paramSender{
NSTimeInterval backgroundTimeRemaining =
[[UIApplication sharedApplication] backgroundTimeRemaining];
if (backgroundTimeRemaining == DBL_MAX)
{
NSLog(#"Background Time Remaining = Undetermined");
}
else
{
NSLog(#"Background Time Remaining = %.02f Seconds",backgroundTimeRemaining);
}
}
- (void) endBackgroundTask
{
dispatch_queue_t mainQueue = dispatch_get_main_queue(); __weak AppDelegate *weakSelf = self;
dispatch_async(mainQueue, ^(void) { AppDelegate *strongSelf = weakSelf; if (strongSelf != nil){
[strongSelf.myTimer invalidate];
[[UIApplication sharedApplication]
endBackgroundTask:self.backgroundTaskIdentifier];
strongSelf.backgroundTaskIdentifier = UIBackgroundTaskInvalid;
} });
}
As pointed out in the comments, NSTimer won't work in the background, backround execution on iOS is quite tricky and only works in certain cases, check the Apple Docs on the topic, also this is an excellent read to acquire more background knowledge.
As for your case, it sound like you want to use UILocalNotification. As I understand from your comment:
I want to have a timer running while the app is not in the foreground. Just like Apples own timer app.
Apple's timer app uses UILocalNotification. It gives you a way to schedule a notification which will appear at a certain point in time to the user, regardless of whether the app is in the foreground or background! All you have to do in your app is schedule a notification, e.g. like this:
UILocalNotification *localNotification = [[UILocalNotification alloc] init];
localNotification.fireDate = dateTime;
localNotification.alertBody = [NSString stringWithFormat:#"Alert Fired at %#", dateTime];
localNotification.soundName = UILocalNotificationDefaultSoundName;
[[UIApplication sharedApplication] scheduleLocalNotification:localNotification];
Then iOS will handle the rest for you :)
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
...
I'm developing a Cordova project using this plugin: Cordova Plugin Background Geolocation
to get GPS updates when my app is in background.
I would like to stop getting updates after 3 minutes the app is on background.
I don't have the Objective-C skills to modify the plugin to achieve this.
I guess that there's a way to use a timer on Objective-C to stop the service after 3 minutes.
Can anyone help me with this?
UPDATE
The plugin has a stop method here.
Try this solution, here you need to create global object of NSTimer and check whether NSTimer object is validate or not
-(void)applicationDidEnterBackground:(UIApplication *)application {
if ([objTimer isValid]) {
[objTimer invalidate];
}else{
objTimer = [NSTimer scheduledTimerWithTimeInterval:180 target:self selector:#selector(stopGPS:) userInfo:nil repeats:NO];
}
objTimer = nil;}
- (void)stopGPS:(NSTimer *)timer {
// code for stoping GPS service.}
You must modify your .plist file to indicate that you will be running tasks once the app has entered background...That being said, what you want might look something like this (all in AppDelegate)
- (void)applicationDidEnterBackground:(UIApplication *)application {
NSLog(#"Entered background");
[self monitorLocation];
}
- (void)monitorLocation
{
[NSTimer scheduledTimerWithTimeInterval:180 //That's in seconds
target:self
selector:#selector(stopMonitoring)
userInfo:nil
repeats:NO];
//Do whatever monitoring here
}
- (void)stopMonitoring
{
//Stop monitoring location
}
- (void)applicationWillEnterForeground:(UIApplication *)application {
NSLog(#"app will enter foreground");
[self stopMonitoring];
}