i know in iOS, if you leave your device idle for 45 seconds, the screen will become dim, if idle for another 15 seconds, the device will be locked automatically.
and we can disable the auto lock by
[UIApplication sharedApplication].idleTimerDisabled = YES
however, i do want this feature, just want to make it longer, is there any method (no jailbreak) that i can manually setup this timer?
thanks
You can achieve this to an extent by monitoring the user's interaction with your app (touch events) and setting [UIApplication sharedApplication].idleTimerDisabled = NO; when a custom idle timer expires.
You can learn more about monitoring events on this blog. However I've outlined the steps below with code updates for ARC.
A bit of background on what is possible after testing this. Apple's internal idle timer runs regardless if idleTimerDisabled = YES; is set. What this means is that if the user has not interacted with the phone past the Auto-Lock setting (i.e. 1 minute) when idleTimerDisabled = NO; is set the device will half dim immediately and turn off completely after 15 seconds. So what we can do is disable idleTimer, and manually create a new timer which waits x minutes before enabling the idleTimer again.
This will effectively allow you to increase the Auto-Lock time. I don't think you can decrease it (i.e. user has Auto-Lock Never, and you want to lock the device after a minute).
With the following code (assuming you have Auto-Lock set to 1 minute) the app will stay awake for 2 minutes, after which we set idleTimerDisabled = NO; dimming the app for 15 seconds before it turns off.
Add the following two files to your project (original source here):
ELCUIApplication.h
//
// ELCUIApplication.h
//
// Created by Brandon Trebitowski on 9/19/11.
// Copyright 2011 ELC Technologies. All rights reserved.
//
#import <Foundation/Foundation.h>
// # of minutes before application times out
#define kApplicationTimeoutInMinutes 2
// Notification that gets sent when the timeout occurs
#define kApplicationDidTimeoutNotification #"ApplicationDidTimeout"
/**
* This is a subclass of UIApplication with the sendEvent: method
* overridden in order to catch all touch events.
*/
#interface ELCUIApplication : UIApplication {
NSTimer *_idleTimer;
}
/**
* Resets the idle timer to its initial state. This method gets called
* every time there is a touch on the screen. It should also be called
* when the user correctly enters their pin to access the application.
*/
- (void)resetIdleTimer;
#end
ELCUIApplication.m
//
// ELCUIApplication.m
//
// Created by Brandon Trebitowski on 9/19/11.
// Copyright 2011 ELC Technologies. All rights reserved.
//
#import "ELCUIApplication.h"
#implementation ELCUIApplication
- (void)sendEvent:(UIEvent *)event {
[super sendEvent:event];
// Fire up the timer upon first event
if(!_idleTimer) {
[self resetIdleTimer];
}
// Check to see if there was a touch event
NSSet *allTouches = [event allTouches];
if ([allTouches count] > 0) {
UITouchPhase phase = ((UITouch *)[allTouches anyObject]).phase;
if (phase == UITouchPhaseBegan) {
[self resetIdleTimer];
}
}
}
- (void)resetIdleTimer {
if (_idleTimer) {
[_idleTimer invalidate];
// [_idleTimer release];
}
// Schedule a timer to fire in kApplicationTimeoutInMinutes * 60
float timeout = kApplicationTimeoutInMinutes * 60;
_idleTimer = [NSTimer scheduledTimerWithTimeInterval:timeout
target:self
selector:#selector(idleTimerExceeded)
userInfo:nil
repeats:NO];
}
- (void)idleTimerExceeded {
/* Post a notification so anyone who subscribes to it can be notified when
* the application times out */
[[NSNotificationCenter defaultCenter]
postNotificationName:kApplicationDidTimeoutNotification object:nil];
}
//- (void) dealloc {
// [_idleTimer release];
// [super dealloc];
//}
#end
In your Supporting Files folder open up main.m, update the following:
#import <UIKit/UIKit.h>
#import "AppDelegate.h"
int main(int argc, char *argv[])
{
#autoreleasepool {
return UIApplicationMain(argc, argv, #"ELCUIApplication", NSStringFromClass([AppDelegate class]));
}
}
In AppDelegate.m edit the didFinishLaunchingWithOptions: method and add the applicationDidTimeout: method.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Override point for customization after application launch.
[UIApplication sharedApplication].idleTimerDisabled = YES;
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(applicationDidTimeout:)
name:kApplicationDidTimeoutNotification object:nil];
return YES;
}
- (void) applicationDidTimeout:(NSNotification *) notif {
NSLog(#"applicationDidTimeout");
[UIApplication sharedApplication].idleTimerDisabled = NO;
}
Related
I'm in the process of developing an iOS app where I need to track if the user leaves the app (presses the home button to use other apps) while they are 'in game' however the user should be able to lock and un-lock their device without this function being called.
func applicationDidEnterBackground(application: UIApplication) {
if defaults.boolForKey("TimerActive"){
defaults.setBool(true, forKey: "Failed")
}
}
This, unfortunately, is triggered when the user locks their devices as well as when they exit the app.
A little context about the app: The app encourages people to focus on their work and not become distracted by their phones for a preset time period.
Other suggestions of how I can encourage the users to reopen the app upon exit while the timer is still active but not when they lock their devices would be greatly welcomed!
Well, there's no clean way to do this. But there is a hack that you can use. It's not guaranteed to keep working though(I've tested up to iOS 9.3 and I'm pretty sure it works on the iOS 10 betas).
The idea is that there's a system-wide notification for the phone being locked. You can listen to that and coupled with listening to background/foreground events on your app you can determine what's happening.
This is a piece of code for an object that will watch for this stuff. Create it from app delegate or wherever and keep a strong ref for as long as you need it. Give it a delegate it can call for whatever events you want to observer and react to(or put the code right there in checkState). I haven't compiled this so I may have made some errors typing it up. It's derived from code I use in an app, but the original has a lot more stuff I won't post here. It's in objc but it shouldn't be too hard to convert to swift(someone feel free to post a second answer in swift or edit mine, but I don't have the time to do it right now)
#interface LockStateDetector : NSObject {
int _notify_token;
}
#property BOOL deviceIsLocked;
#property BOOL appIsInBackground;
#property NSTimer * checkStateTimer;
#end
#implementation LockStateDetector
- (instancetype)init
{
self = [super init];
if (self) {
[self registerForNotifications];
}
return self;
}
- (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
notify_cancel(_notify_token);
}
- (void)registerForNotifications
{
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(didMoveToBackground) name:UIApplicationDidEnterBackgroundNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(didBecomeActive) name:UIApplicationDidBecomeActiveNotification object:nil];
__weak__ LockStateDector * wSelf = self;
notify_register_dispatch("com.apple.springboard.lockstate", &_notify_token, dispatch_get_main_queue(), ^(int token) {
__strong__ LockStateDetector sSelf = wSelf;
if (!sSelf) {
return;
}
uint64_t state = UINT64_MAX;
notify_get_state(token, &state);
sSelf.deviceIsLocked = state != 0;
NSLog(#"device lock state changed: %#", #(state));
[sSelf checkState];
});
}
- (void)didBecomeActive
{
self.appIsInBackground = NO;
[self checkState];
}
- (void)didMoveToBackground
{
self.appIsInBackground = YES;
[self checkState];
}
- (void)checkState
{
[self.checkStateTimer invalidate];
self.checkStateTimer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector(_checkState) userInfo:nil repeats:NO];
}
- (void)_checkState
{
[self.checkStateTimer invalidate];
self.checkStateTimer = nil;
if (!self.appIsInBackground) {
return;
}
if (!self.deviceIsLocked) {
// app is in background because device was locked
} else {
// app is in background because user pressed home and switched to something else
}
}
What I need to do is this; I will have a timer that will tick away and when 30 minutes is up I'll auto signout the user. But if there's any interaction with the application I will reset the timer to 30 min. I have an idea on what to do but I'm sure there's a better way to accomplish this.
What I'll do is make a singleton class that holds a timer and posts a notification when the timer is up. So what I'm thinking is I'll have to reset the timer when ever the user presses a button, goes to the next screen etc.
My quesiton though is is it possible to respond to any touches in the app in one piece of code? Like somehow there's a superclass I can add this to and it will always reset the timer no matter what kind of interaction has happened? Or do I need to add the code to all the places where the user will interact with the application?
You can try this, subclass UIApplication and add following code in implementation
#implementation MyApplication
- (instancetype)init {
self = [super init];
if (self) {
[self reset];
}
return self;
}
- (void)reset {
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:#selector(logout) object:nil];
[self performSelector:#selector(logout) withObject:nil afterDelay:30*60];
}
- (void)sendEvent:(UIEvent *)event {
[super sendEvent:event];
[self reset];
NSLog(#"event detected");
}
- (void)logout {
NSLog(#"logout now");
}
#end
Then in main.m change the implementation like this
return UIApplicationMain(argc, argv, NSStringFromClass([MyApplication class]), NSStringFromClass([AppDelegate class]));
Here what is happening is, - (void)sendEvent:(UIEvent *)event method will get called after each user activity, Then we are registering a perform selector request after 30 mins. Once user touches the screen within 30 mins cancel previous request and register new one.
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
...
I would like to autologout my app incase if the user doesn't interact with it for five minutes just like web user session in websites.
I tried all over the internet I couldn't find anything useful.
I am beginner to this development.
Any help is appreciated !
Pseudo code:
Have a timer running that invokes a method after time is up(session time-out period).
The method should navigate to the Login activity
Reset the timer if there is any user interaction with the app.
Code:
1) Create a new file -> Objective-C class -> type in a name (in my case SessionApplication)
2) Change the subclass to UIApplication. You may have to manually type this in the subclass field.
You should now have the appropriate .h and .m files.
Change the .h file to read as follows:
#import <Foundation/Foundation.h>
//the length of time before your application "times out". This number actually represents seconds, so we'll have to multiple it by 60 in the .m file
#define SessionTimeoutPeriodMins 5
//the notification your AppDelegate needs to watch for in order to know that it has indeed "timed out"
#define kapplicationDidSessionTimeoutNotification #"AppTimeOut"
#interface SessionApplication : UIApplication
{
NSTimer *mySessionTimer;
}
-(void)resetSessionTimer;
#end
Change the .m file to read as follows:
#import "SessionApplication.h"
#implementation SessionApplication
//here we are listening for any touch. If the screen receives touch, the timer is reset
-(void)sendEvent:(UIEvent *)event
{
[super sendEvent:event];
if (!mySessionTimer)
{
[self resetSessionTimer];
}
NSSet *allTouches = [event allTouches];
if ([allTouches count] > 0)
{
UITouchPhase phase = ((UITouch *)[allTouches anyObject]).phase;
if (phase == UITouchPhaseBegan)
{
[self resetSessionTimer];
}
}
}
//as labeled...reset the timer
-(void)resetSessionTimer
{
if (mySessionTimer)
{
[mySessionTimer invalidate];
}
//convert the wait period into minutes rather than seconds
int timeout = SessionTimeoutPeriodMins * 60;
mySessionTimer = [NSTimer scheduledTimerWithTimeInterval:timeout target:self selector:#selector(sessionTimedOut) userInfo:nil repeats:NO];
}
//if the timer reaches the limit as defined in SessionTimeoutPeriodMins, post this notification
-(void)sessionTimedOut
{
[[NSNotificationCenter defaultCenter] postNotificationName:kapplicationDidSessionTimeoutNotification object:nil];
}
#end
Go into your Supporting Files folder and alter main.m to this (different from prior versions of XCode):
#import <UIKit/UIKit.h>
#import "AppDelegate.h"
#import "SessionApplication.h"
int main(int argc, char *argv[])
{
#autoreleasepool {
return UIApplicationMain(argc, argv, NSStringFromClass([SessionApplication class]), NSStringFromClass([AppDelegate class]));
}
}
Write the remaining code in your AppDelegate.m file. I've left out code not pertaining to this process. There is no change to make in the .h file.
#import "AppDelegate.h"
#import "SessionApplication.h"
#implementation AppDelegate
#synthesize window = _window;
-(BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(applicationDidSessionTimeout:) name:kapplicationDidSessionTimeoutNotification object:nil];
return YES;
}
-(void)applicationDidSessionTimeout:(NSNotification *) notif
{
NSLog (#“session timed out!!");
//Get the controller to login activity
}
Reference
Sample