Skip to content

Instantly share code, notes, and snippets.

Created October 20, 2014 23:01
Show Gist options
  • Save anonymous/e4082bb0291c59660ee2 to your computer and use it in GitHub Desktop.
Save anonymous/e4082bb0291c59660ee2 to your computer and use it in GitHub Desktop.
EXEC_BAD_ACCESS on idle time out
// AppDelegate.h
#import <UIKit/UIKit.h>
#import "ViewController.h"
extern NSString * const kDateSinceAppSuspend;
extern NSString * const kStopTimer;
extern NSString * const kSuspendedDateUserInfo;
extern NSString * const kCurrentDate;
extern NSString * const kInvalidateTimer;
extern NSString * const kLogoutEvent;
extern NSString * const kResetEvent;
extern NSString * const kDSRAppName;
extern NSString * const kSMCookieName;
extern NSString * const kDomainName;
extern NSString * const kAddress2;
extern NSString * const kAddress1;
extern NSString * const kServiceOrderImagePath;
extern NSString * const kServiceOrderSignature;
extern NSString * const kContractSignature;
extern NSString * const kContractSignName;
extern NSString * const kServiceSignName;
@interface AppDelegate : UIResponder <UIApplicationDelegate>
@property (strong, nonatomic) UIWindow *window;
@property (strong, nonatomic) NSOperationQueue *queue;
@property (strong, nonatomic) ViewController *viewController;
- (void) startTimer;
- (void) stopTimer;
- (void) resetTimer;
@end
//Appdelegate.m
#import "AppDelegate.h"
#import "TimerFactory.h"
#import "SPTimer.h"
NSString * const kDateSinceAppSuspend = @"date_suspended";
NSString * const kStopTimer = @"stop_timer";
NSString * const kSuspendedDateUserInfo = @"suspended_date_userinfo";
NSString * const kCurrentDate = @"current_date";
NSString * const kInvalidateTimer = @"invalidate_timer";
NSString * const kLogoutEvent = @"logOut()";
NSString * const kResetEvent = @"resetTimeOut()";
NSString * const kDSRAppName = @"salesportal";
NSString * const kSMCookieName = @"TestInternalSMSESSION";
NSString * const kDomainName = @"comcast.com";
NSString * const kAddress2 = @"Address2";
NSString * const kAddress1 = @"Address1";
NSString * const kServiceOrderImagePath = @"service_order_img";
NSString * const kServiceOrderSignature = @"SERVICE_ORDER_SIGN";
NSString * const kContractSignature = @"CONTRACT_SIGN";
NSString * const kContractSignName = @"img_contracts.JPG";
NSString * const kServiceSignName = @"img_service.JPG";
@interface AppDelegate ()
@end
@implementation AppDelegate
@synthesize queue;
@synthesize viewController;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
self.queue = [NSOperationQueue mainQueue];
self.viewController = [[ViewController alloc] init];
self.window.rootViewController = self.viewController;
[self.window makeKeyAndVisible];
return YES;
}
- (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.
}
- (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 inactive 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:.
}
- (void) startTimer {
SPTimer *s_timer = [SPTimer sharedInstance];
TimerFactory *factory = [TimerFactory factory];
[[factory getTimerFor:kSPTimerIdle withTimeout:.1] startWithCompletionHandler:^(NSTimer *timer) {
[timer invalidate];
timer = nil;
[s_timer showIdleAlert];
} AndExceedTimeoutHandler:^(NSTimer *timer) {
[timer invalidate];
timer = nil;
[s_timer showCountownAlert];
}];
}
- (void) stopTimer {
NSLog(@"Stop Timer");
[[NSNotificationCenter defaultCenter] postNotificationName:kInvalidateTimer object:nil];
}
- (void) resetTimer {
[self stopTimer];
[self startTimer];
}
//SPTimer.h
#import <Foundation/Foundation.h>
typedef void (^ CountDownTimerBlock)(NSInteger);
typedef void (^ IdleAlertBlock)(NSInteger);
@interface SPTimer : NSObject {
}
+ (id) sharedInstance;
- (void) showIdleAlert;
- (void) showCountownAlert;
//SPTimer.m
#import "SPTimer.h"
#import "SPTimer.h"
#import "TimerFactory.h"
#import <objc/runtime.h>
#import "AppDelegate.h"
#import "UIAlertMode.h"
#import "SessionManager.h"
@interface SPTimer () {
UIAlertMode *_alertMode;
SessionManager *_sessionManager;
}
@property (nonatomic, assign) BOOL p_idleTimerRunning;
@property (nonatomic, assign) BOOL p_countdownTimerRunning;
@property (nonatomic, strong) NSTimer *p_idleTimer;
@property (nonatomic, strong) NSTimer *p_countdownTimer;
@property (nonatomic, copy) CountDownTimerBlock p_countDownTimerBlock;
@property (nonatomic, copy) IdleAlertBlock p_idleAlertBlock;
@end
@implementation SPTimer
//constants
static const void *kIdleTimerKey = @"IdleTimerKey";
static const void *kCountdownTimerKey = @"CountdownTimerKey";
@synthesize p_idleTimerRunning, p_countdownTimerRunning, p_idleTimer, p_countdownTimer, p_countDownTimerBlock, p_idleAlertBlock;
- (id) init {
self = [super init];
if(self) {
}
return self;
}
//creating a singleton using dispatch_once is faster than using the @synchronize directive
+ (id) sharedInstance {
static SPTimer *sharedInstance = nil;
static dispatch_once_t once_t;
dispatch_once(&once_t, ^{
sharedInstance = [[SPTimer alloc] init];
});
return sharedInstance;
}
- (void) showIdleAlert {
_alertMode = [[UIAlertMode alloc] initAlertWith:@"Alert" :@"Your session will expire in 60 seconds" :@"OK" :@"Continue" ];
__block AppDelegate *delegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
_sessionManager = [[SessionManager alloc] init];
if ([_alertMode supportsiOS8]) {
[_alertMode showAlertControllerWith:^(UIAlertAction *defaultAction) {
NSLog(@"log out clicked");
[delegate stopTimer];
} And:^(UIAlertAction *cancelAction) {
NSLog(@"reset clicked");
[delegate resetTimer];
}];
} else {
[_alertMode showAlertWith:^(NSInteger idx) {
if (idx == 0) {
NSLog(@"log out clicked");
[delegate stopTimer];
} else {
NSLog(@"reset clicked");
[delegate resetTimer];
}
}];
}
__block NSTimer *tmpTimer;
TimerFactory *factory = [TimerFactory factory];
[[factory getTimerFor:kSPTimerCountdown withTimeout:1] startWithCompletionHandler:^(NSTimer *timer){
NSLog(@"countdown completed");
[timer invalidate];
//[self showCountownAlert];
} AndUpdateHandler:^(NSInteger idx, NSTimer *timer) {
tmpTimer = timer;
if(idx <= 0) {
}
[_alertMode setAlertMessage:[NSString stringWithFormat:@"Your session will expire in %ld seconds", (long)idx]];
}];
}
- (void) showCountownAlert {
self.p_countDownTimerBlock = ^ (NSInteger idx){
if(idx == 0) {
//AppDelegate *delegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
NSLog(@"you will be redirected to the login screen");
}
};
}
- (void) alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
//get the idle timer block here
void (^idleBlock) (NSInteger) = objc_getAssociatedObject(alertView, kIdleTimerKey);
if(idleBlock)
idleBlock(buttonIndex);
//get the countdown timer block here
void (^countdownBlock) (NSInteger) = objc_getAssociatedObject(alertView, kCountdownTimerKey);
if(countdownBlock)
countdownBlock(buttonIndex);
}
@end
// TimerFactor.h
#import <Foundation/Foundation.h>
typedef void (^IdleTimerCompletionHandler) (NSTimer *);
typedef void (^IdleTimerExceedHandler) (NSTimer *);
typedef void (^CountDownTimerCompletionHandler)(NSTimer *);
typedef void (^UpdateTimer)(NSInteger, NSTimer *);
typedef NS_ENUM(NSInteger, SPTimerType) {
kSPTimerIdle,
kSPTimerCountdown
};
@interface TimerFactory : NSObject
@property (nonatomic, copy) IdleTimerCompletionHandler completionHandler;
@property (nonatomic, copy) IdleTimerExceedHandler timeoutExceedHandler;
@property (nonatomic, copy) CountDownTimerCompletionHandler completionHandler_;
@property (nonatomic, copy) UpdateTimer updateHandler;
+ (TimerFactory *)factory;
- (TimerFactory *) getTimerFor:(SPTimerType) type withTimeout:(NSTimeInterval)timeout;
- (void) startWithCompletionHandler:(IdleTimerCompletionHandler) completion AndExceedTimeoutHandler:(IdleTimerExceedHandler) exceed;
- (void) startWithCompletionHandler:(CountDownTimerCompletionHandler) completion AndUpdateHandler:(UpdateTimer)updateHandler;
@end
//TimerFactory.m
#import "TimerFactory.h"
#import "SPIdleTimer.h"
#import "SPCountDownTimer.h"
@implementation TimerFactory
@synthesize completionHandler, completionHandler_, updateHandler, timeoutExceedHandler;
+ (TimerFactory *)factory {
return [[[self class] alloc] init];
}
- (TimerFactory *) getTimerFor:(SPTimerType) type withTimeout:(NSTimeInterval)timeout {
TimerFactory *factory = nil;
switch (type) {
case kSPTimerIdle:
factory = [[SPIdleTimer alloc] initWithTimeout:timeout];
break;
case kSPTimerCountdown:
factory = [[SPCountDownTimer alloc] initWithTimeout:timeout];
break;
default:
break;
}
return factory;
}
- (void) startWithCompletionHandler:(IdleTimerCompletionHandler) completion AndExceedTimeoutHandler:(IdleTimerExceedHandler)exceed {
NSAssert(NO, @"subclasses of timer factory should implement this method");
}
- (void) startWithCompletionHandler:(CountDownTimerCompletionHandler) completion AndUpdateHandler:(UpdateTimer)updateHandler {
NSAssert(NO, @"subclasses of timer factory should implement this method");
}
@end
//SPIdleTimer.h
#import <Foundation/Foundation.h>
#import "TimerFactory.h"
@interface SPIdleTimer : TimerFactory {
NSTimeInterval timeOut_;
NSTimeInterval intervalSinceStart_;
BOOL isRunning_;
NSTimer *idleTimer_;
}
- (id) initWithTimeout:(NSTimeInterval) timeOut;
@end
//SPIdleTimer.m
#import "SPIdleTimer.h"
#import "AppDelegate.h"
#import "SPTimer.h"
@implementation SPIdleTimer
- (id) initWithTimeout:(NSTimeInterval) timeOut {
self = [super init];
if(self) {
timeOut_ = timeOut;
intervalSinceStart_ = 0.0;
}
return self;
}
- (void) startWithCompletionHandler:(IdleTimerCompletionHandler) completion AndExceedTimeoutHandler:(IdleTimerExceedHandler)exceed {
self.completionHandler = completion;
self.timeoutExceedHandler = exceed;
isRunning_ = YES;
AppDelegate *delegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
[[NSNotificationCenter defaultCenter] addObserverForName:kDateSinceAppSuspend object:nil queue:[delegate queue] usingBlock:^(NSNotification *note) {
NSDictionary *d = [note userInfo];
NSDate *dateSince = [d objectForKey:kSuspendedDateUserInfo];
NSTimeInterval timeSince = [[NSDate date] timeIntervalSinceDate:dateSince];
if(timeSince > 0.0) {
intervalSinceStart_ += (int)timeSince;
}
}];
[[NSNotificationCenter defaultCenter] addObserverForName:kInvalidateTimer object:nil queue:[delegate queue] usingBlock:^(NSNotification *note) {
intervalSinceStart_ = 0.0;
}];
idleTimer_ = [NSTimer scheduledTimerWithTimeInterval:0.5 target:self selector:@selector(updateIdleTimer:) userInfo:nil repeats:YES];
}
#pragma mark - update selectors
- (void) updateIdleTimer : (NSTimer *) timer {
intervalSinceStart_ += [timer timeInterval];
//NSLog(@"timer interval %f", intervalSinceStart_);
AppDelegate *delegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
/*if (! delegate.loggedIn) {
[timer invalidate];
timer = nil;
}*/
if(intervalSinceStart_ == timeOut_ * 60) {
isRunning_ = NO;
self.completionHandler(timer);
} else if(intervalSinceStart_ > timeOut_ * 60) {
isRunning_ = NO;
self.timeoutExceedHandler(timer);
}
}
@end
//SPCountDownTimer.h
#import <Foundation/Foundation.h>
#import "TimerFactory.h"
@interface SPCountDownTimer : TimerFactory {
NSTimeInterval timeOut_;
NSInteger toTimeOut_;
BOOL isRunning_;
}
- (id) initWithTimeout:(NSTimeInterval) timeOut;
@end
//SPCountDownTimer.m
#import "SPCountDownTimer.h"
#import "AppDelegate.h"
@interface SPCountDownTimer () {
NSTimer *_timerCountDown;
}
- (void) stopTimer:(NSTimer *)timer;
@end
@implementation SPCountDownTimer
- (id) initWithTimeout : (NSTimeInterval) timeout {
self = [super init];
if(self) {
timeOut_ = timeout;
}
return self;
}
- (void) startWithCompletionHandler:(CountDownTimerCompletionHandler) completion AndUpdateHandler:(UpdateTimer)updateHandler {
self.completionHandler_ = completion;
self.updateHandler = updateHandler;
toTimeOut_ = 60;
isRunning_ = YES;
AppDelegate *delegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
[[NSNotificationCenter defaultCenter] addObserverForName:kDateSinceAppSuspend object:nil queue:[delegate queue] usingBlock:^(NSNotification *note) {
NSDictionary *d = [note userInfo];
NSDate *dateSince = [d objectForKey:kSuspendedDateUserInfo];
NSTimeInterval timeSince = [[NSDate date] timeIntervalSinceDate:dateSince];
if(timeSince > 0.0) {
toTimeOut_ -= (int)timeSince;
}
}];
[[NSNotificationCenter defaultCenter] addObserverForName:kStopTimer object:nil queue:[delegate queue] usingBlock:^(NSNotification *note) {
[self stopTimer:_timerCountDown];
}];
_timerCountDown = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(updateCountDown:) userInfo:nil repeats:YES];
}
#pragma mark - update selectors
- (void) updateCountDown:(NSTimer *) timer {
toTimeOut_ -= 1;
NSLog(@"to time out %ld", (long)toTimeOut_);
self.updateHandler(toTimeOut_, timer);
if(toTimeOut_ <= 0) {
[self stopTimer:timer];
isRunning_ = NO;
if(self.completionHandler_)
self.completionHandler_(timer);
}
}
#pragma mark - private methods
- (void) stopTimer:(NSTimer *)timer {
[timer invalidate];
timer = nil;
}
@end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment