NSUserDefaults not updating value quickly - ios

I have at timeout feature where if the app is idle (in background) for a period of time, I timeout my app and send the user to the login screen. I set the "timedOut" key in user defaults to YES in my application delegate, then reference that key in each view controller, where if it is YES, I segue to the login screen. On the login screen I have a label that displays "Session has timed out" if the "timedOut" is YES. My issue is that if I login, then logout very quickly, the label is displayed, even though I explicitly set that key to NO right after I show the label and then synchronize the user defaults. If I wait a second or two and logout, the label is hidden like it should be. I have solved the "problem", but would like to understand the behavior.
Code from view did load in my login view controller. You would think this changes the isTimedOut to NO, but when I do a quick logout viewdidload is called again, but isTimedOut is YES.
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
sessionLabel.hidden = YES;
isTimedOut = [defaults boolForKey:#"isTimedOut"];
if (isTimedOut == YES)
{
sessionLabel.hidden = NO;
defaults = [NSUserDefaults standardUserDefaults];
[defaults setBool:NO forKey:#"isTimedOut"];
isTimedOut = NO;
NSLog(#"Timed Out has been reset to %s",[defaults boolForKey:#"isTimedOut"] ? "YES" : "NO");
[defaults synchronize];
}
UPDATE
I replaced the code above using a property in my app delegate instead of NSUserDefaults and the "strange" behavior went away.
eONavAppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
isTimedOut = appDelegate.isTimedOut;
sessionLabel.hidden = YES;
//isTimedOut = [defaults boolForKey:#"isTimedOut"];
NSLog(#"Timed Out has been reset to %s",appDelegate.isTimedOut ? "YES" : "NO");
if (isTimedOut == YES)
{
appDelegate.isTimedOut = NO;
sessionLabel.hidden = NO;
}
MORE CODE
To logout, I have UIButtonBarItem calling a segue programmatically. The doLogout property tells the login view controller to run a logout API.
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
// Make sure your segue name in storyboard is the same as this line
if ([[segue identifier] isEqualToString:#"logoutSegue"])
{
// Get reference to the destination view controller
eoLoginViewController *vc = [segue destinationViewController];
vc.doLogout = YES;
}
}
isTimedOut is set in one location in the app delegate.
-(void)timeoutWithDate:(NSDate *)currentDate
{
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSDate *previousDate = [defaults objectForKey:#"enteredBackground"];
NSTimeInterval distanceBetweenDates = [currentDate timeIntervalSinceDate:previousDate];//TimeInterval is in seconds
NSLog(#"Time between dates in seconds %f",distanceBetweenDates);
double minutesInAnHour = 60;
double minutesBetweenDates = distanceBetweenDates / minutesInAnHour;
NSLog(#"minutesBetweenDates %f",minutesBetweenDates);
if(minutesBetweenDates > 60)
{
isTimedOut = YES;
}
else
{
isTimedOut = NO;
}
}

Use this:
To save a Bool:
[[NSUserDefaults standardUserDefaults] setBool:NO forKey:#"isTimedOut"];
To Load a Bool:
if ([[NSUserDefaults standardUserDefaults] boolForKey:#"isTimedOut"] == YES) {
//it equals yes
}
else {
//it equals no
}

why don't you try something like this? I didn't see your full project but it should work like a charm.
- (void)viewDidLoad {
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(applicationWillResignActive:) name:UIApplicationWillResignActiveNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(applicationWillEnterForeground:) name:UIApplicationWillEnterForegroundNotification object:nil];
}
then...
- (void)applicationWillResignActive:(UIApplication *)application
{
[[NSUserDefaults standardUserDefaults] setValue:[NSDate date] forKey:#"sleepTime"];
[[NSUserDefaults standardUserDefaults] synchronize];
}
and...
- (void)applicationWillEnterForeground:(UIApplication *)application
{
NSInteger _timeoutInSeconds = 300;
NSDate *_sleepTime = [[NSUserDefaults standardUserDefaults] valueForKey:#"sleepTime"];
if (_sleepTime) {
NSCalendar *_calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
NSDateComponents *_dateComponents = [_calendar components:NSSecondCalendarUnit fromDate:_sleepTime toDate:[NSDate date] options:0];
if (_dateComponents.second > _timeoutInSeconds) {
// expired session
} else {
// valid session
}
}
}

Related

Xcode NSUserDefaults settings lost when app is force quit

I'm using NSUserDefaults to save BOOLs that denote whether a mapped annotation is a "favorite". The BOOLs are saved correctly when the User quits the app and then re-launches it. However, when the User force quits (press the Home button twice and swipe up on the app) the app, the NSUserDefaults are lost. Can someone please explain why this is happening? I'm using Xcode v7.0.
-(void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
// If annotation is displayed, the User can save/clear annotation as a favorite
if ((_acmeMotorsIsDisplayed == YES) && (_acmeMotorsIsFavorite == YES)){
_acmeMotorsIsFavorite = NO;
[[NSUserDefaults standardUserDefaults] setBool:NO forKey:#"acmeMotorsIsFavorite"];
}
else if ((_acmeMotorsIsDisplayed == YES) && (_acmeMotorsIsFavorite == NO)){
_acmeMotorsIsFavorite = YES;
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:#"acmeMotorsIsFavorite"];
}
}
-(void)loadUserDefaults{
// One of many lines of code that load user default settings
_acmeMotorsIsFavorite = [[NSUserDefaults standardUserDefaults] boolForKey:#"acmeMotorsIsFavorite"];
}
-(void)viewDidLoad {
[self loadUserDefaults];
}
I think you forget about synchronize method.
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
[userDefaults setBool:NO forKey:#"acmeMotorsIsFavorite"];
[userDefaults synchronize];
When the user double taps Home the app delegate method applicationWillResignActive: is called, at that time synchronize NSUserDefaults:
- (void)applicationWillResignActive:(UIApplication *)application {
[[NSUserDefaults standardUserDefaults] synchronize];
}

How to launch a different viewController on the second launch

Here the code I have under the viewController.m
This code will run when a user selects a viewController
-(void)switchViews {
UIStoryboard *mainStory = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
UIViewController *vc = [mainStory instantiateViewControllerWithIdentifier:schoolName];
[self presentModalViewController:vc animated:YES];
NSUserDefaults *defaultViewController = [NSUserDefaults standardUserDefaults];
[defaultViewController setObject:nil forKey:#"save"];
[defaultViewController synchronize];
}
This code will run on the second time the app is launched
-(void)loadNewView {
UIStoryboard *mainStory = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
NSUserDefaults *newUserDefault = [NSUserDefaults standardUserDefaults];
NSString *newVC = [NSString stringWithFormat:#"save", schoolName];
UIViewController *newViewController = [mainStory instantiateViewControllerWithIdentifier:newVC];
[self presentModalViewController:newViewController animated:YES];
}
Keeping in mind that schoolNameis a string
How can I run [self loadNewView] under the viewDidLoad but running it on the second time the app is launched?
You should do something like
id value = [[NSUserDefaults standardUserDefaults] objectForKey:#"save"];
if (value) {
UIViewController *vc = [mainStory instantiateViewControllerWithIdentifier:value];
[self presentModalViewController:vc animated:YES];
} else {
// first time logic
}
in your AppDelegate or wherever you have your navigation logic. You should not have that logic in viewDidLoad.
-(void)viewDidLoad {
[super viewDidLoad];
NSString *launchCount = #"LaunchCount";
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
NSInteger count;
if([userDefaults objectForKey:launchCount]) {
count = [userDefaults integerForKey:launchCount];
}
else {
count = 0;
}
count++; //increment the launch count
[userDefaults setObject:[NSNumber numberWithInt:count] forKey:launchCount];
[userDefaults synchronize];
if([userDefaults integerForKey:launchCount] >= 2) {
// Do your thang
}
}
on ViewDidLoad function do something like this
if (![[NSUserDefaults standardUserDefaults] boolForKey:#"HasLaunchedFirst"])
{
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:#"HasLauncheFirst"];
[[NSUserDefaults standardUserDefaults] synchronize];
[self switchViews];
}
else
{
[[NSUserDefaults standardUserDefaults] setBool:No forKey:#"HasLauncheFirst"];
[[NSUserDefaults standardUserDefaults] synchronize];
[self loadNewView];
}

UILocalNotification and UISwitch remember state

I made an UISwitch, and when it is switched on, my app sends 10 notifications with a "repeat interval". It works all fine. But the problem is, when I want to save (remember) the state of my UISwitch and load it, the UILocalNotification aren't being sent anymore.
This is my code:
For the save of the UISwitch state and notification launch:
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
BOOL test= [[NSUserDefaults standardUserDefaults] boolForKey:#"switch"];
NSLog(#"%#",test?#"YES":#"NO");
[self.alarmSwitch setOn:test animated:YES];
}
- (void) switchIsChanged:(UISwitch *)paramSender{
if ([paramSender isOn]){
[[NSUserDefaults standardUserDefaults] setBool:self.alarmSwitch.on forKey:#"switch"];
[[NSUserDefaults standardUserDefaults] synchronize];
notification1 = [[UILocalNotification alloc]init];
notification1.fireDate = [NSDate dateWithTimeIntervalSinceNow:5.0];
[notification1 setAlertBody:#"U moet uw voeten controleren!"];
[[UIApplication sharedApplication] scheduleLocalNotification:notification1];
notification1.fireDate = [NSDate dateWithTimeIntervalSinceNow:10.0];
[[UIApplication sharedApplication] scheduleLocalNotification:notification1];
}
}
How can I save the state of the UISwitch in another way in order to get this work? Or is there another solution to fix this?
Your switchIsChanged: method isn't going to be firing when you set the on state programatically. I would recommend moving the code that handles the notifications out of the switchIsChanged: method. Example:
-(void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
BOOL test= [[NSUserDefaults standardUserDefaults] boolForKey:#"switch"];
NSLog(#"%#",test?#"YES":#"NO");
[self.alarmSwitch setOn:test animated:YES];
// initially detect if we should fire the bnotifications
if ([self.alarmSwitch isOn]) {
[self fireNotifications];
}
}
-(void)switchIsChanged:(UISwitch *)paramSender{
[[NSUserDefaults standardUserDefaults] setBool:paramSender.on forKey:#"switch"];
[[NSUserDefaults standardUserDefaults] synchronize];
if ([paramSender isOn]){
[self fireNotifications];
}
}
-(void)fireNotifications {
notification1 = [[UILocalNotification alloc]init];
notification1.fireDate = [NSDate dateWithTimeIntervalSinceNow:5.0];
[notification1 setAlertBody:#"U moet uw voeten controleren!"];
[[UIApplication sharedApplication] scheduleLocalNotification:notification1];
notification1.fireDate = [NSDate dateWithTimeIntervalSinceNow:10.0];
[[UIApplication sharedApplication] scheduleLocalNotification:notification1];
}
Note: you may have to make sure that the notifications aren't firing more than once which may cause bugs since viewWillAppear: can be called multiple times.

Settings page to show only once

I have an app which should only show the settings page once: when the app is opened for the first time.
Now this works, and when the user presses the middle button on the iPhone it then reopens the app and carries on from the main screen - that's great. But if I double click on the iPhone button and swipe the application off, it will then go to the settings screen again and not to where it was.
Why is it doing that? How can I make my app only show its settings once?
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
NSUserDefaults *settingsscreen = [NSUserDefaults standardUserDefaults];
[settingsscreen registerDefaults:[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:YES],#"firstTime", nil]];
BOOL firstTime = [settingsscreen boolForKey:#"firstTime"];
if ( firstTime==YES) {
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:#"SettingsShown"];
[[NSUserDefaults standardUserDefaults] synchronize];
self.window.rootViewController = [self.window.rootViewController.storyboard instantiateViewControllerWithIdentifier:#"SetUpNav"];
}
else
{
return YES;
}
}
Don't use if ( firstTime==YES) { based on #"firstTime", because that flag is never actually saved. You should be using the flag saved with the #"SettingsShown" key.
BOOL firstTime = [settingsscreen boolForKey:#"SettingsShown"];
if (!firstTime) {
...
Instead doing in appDelegate try to acheive it in setting Page itself
-(void) viewDidLoad {
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
username = [defaults objectForKey:#"username"];
if (username != NULL ) {
[self selfLogin];
}
}
-(void)selfLogin{
nextPageController = [[NextPageViewController alloc]init];
[self.navigationController pushViewController:nextPageController animated:YES];
}

How can i detect 3rd time app launch

I want to show an alertview on third time my app is launched. I have searched everywhere but can't find the solution. How can I know that my app has been launched for the third time?
Thanks in Advance
you can use these methods to read and write to file with very little effort
+ (void) setSetting: (NSString *)key value: (NSString *)value {
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:value forKey:[NSString stringWithFormat:#"%#", key]];
[defaults synchronize];
}
+ (NSString *) getSetting: (NSString *)key defaultVal: (NSString *)defaultVal {
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSString *setting = [defaults stringForKey:[NSString stringWithFormat:#"%#", key]];
if (setting == nil) {
setting = defaultVal;
}
return setting;
}
you can just update a value you have stored in the user defaults each time the didFinishLaunchingWithOptions: in your appDelegate is triggered
Thats easy. Save an NSNumber in your NSUserDefaults and increase it every time you're launching your application. When it hits 3 then show an alert.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)options {
// ...
if ([self plusPlusLaunchCount] == 3) {
[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;
}

Resources