I have a Root.plist file which is used for my app's settings. It has a toggle switch with an identifier of reset_achievements_preference. In the applicationDidBecomeActive method, I have this code:
- (void)applicationDidBecomeActive:(UIApplication *)application
{
NSLog(#"reset achievements: %i", [[NSUserDefaults standardUserDefaults] boolForKey:#"reset_achievements_preference"]);
if ([[NSUserDefaults standardUserDefaults] boolForKey:#"reset_achievements_preference"]) {
[[NSUserDefaults standardUserDefaults] setBool:NO forKey:#"reset_achievements_preference"];
[[NSUserDefaults standardUserDefaults] synchronize];
//Code to react to this change
}
}
Sometimes it hits the NSLog and notices the object change, but sometimes it doesn't. I wonder if I'm dealing with this incorrectly?
Try adding:
[[NSUserDefaults standardUserDefaults] synchronize]
To applicationDidBecomeActive: before anything else to refresh the status of the user defaults. Thy synchronize method is called periodically by the application, but you can refresh it manually.
When do you want to handle your reset_achievements_preference option? The method applicationDidBecomeActive is called when the application starts and when the application returns from background (is brought to foreground by the user).
If you want to handle reset_achievements_preference only at application startup, it is possible that the user puts your app into background, then returns to it. In this case applicationDidBecomeActive is called and it sets reset_achievements_preference to NO which probably is not what you want.
You can simply move this code to application:didFinishLaunchingWithOptions: method to solve this problem.
Related
My UI uses animation of a UILabel to indicate a particular state. I turn that state on and off by using a key in NSUserDefaults standardUserDefaults. The animation restarts correctly (in the simulator) when the app becomes active after simulating the Home button and then clicking on the app. But it doesn't restart correctly if I simulate the Lock button and then click Home. Both events show in the console and the method -(void)startFlashingButton is called in both cases. I can't figure out why it works in one case and not in the other. I would appreciate any help.
Edit 2/14/17: I read several posts on other boards relating to UINotification. It is apparently very difficult to get to work. My animation gets triggered when a notification comes in to the app delegate. I wanted to set up a kind of "do not disturb" system that would silence notifications at a particular time. My understanding is that this cannot be automated by UINotification because the app does not see a notification unless it is front and center OR it is in the background and the user taps on one of the action buttons. If the app is in background and the user ignores the alert, the app will not get triggered and thus has no way of knowing if the do not disturb time has been reached. Also, you can't do this reliably with an NSTimer because the timer won't be recognized when the app is suspended, which it would be shortly after going to background. So, to put it briefly, I have a much bigger problem than the simulation not running. But thanks to all of you for your replies.
Here is the startFlashingButton method:
-(void) startFlashingButton
{
NSUserDefaults *storage = [NSUserDefaults standardUserDefaults];
if([storage boolForKey:#"animation started"] == YES){
// Fade out the view right away
[UIView animateWithDuration:2.0
delay: 0.0
options: (UIViewAnimationOptionCurveEaseIn | UIViewAnimationOptionRepeat | UIViewAnimationOptionAllowUserInteraction)
animations:^{
self.timerLabel.alpha = 0.0;
}
completion:^(BOOL finished){
// Wait one second and then fade in the view
[UIView animateWithDuration:1.0
delay: 2.0
options:UIViewAnimationOptionCurveEaseOut
animations:^{
self.timerLabel.alpha = 1.0;
}
completion:nil];
}];
}
[storage synchronize];
}
In -viewDidLoad, I set up the app to be notified when it becomes active as follows:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(startFlashingButton) name:UIApplicationDidBecomeActiveNotification object:nil];
In the app delegate, I synchonize the user defaults when the app enters background as follows:
- (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.
NSLog(#"application did enter background");
NSUserDefaults *storage = [NSUserDefaults standardUserDefaults];
[storage synchronize];
As Apple's documentation on background transition cycle states
When the user presses the Home button, presses the Sleep/Wake button, or the system launches another app, the foreground app transitions to the inactive state and then to the background state. These transitions result in calls to the app delegate’s applicationWillResignActive: and applicationDidEnterBackground: methods.
Hence The app might resign being active but not being in the background when you lock and then press the home button.
Try creating this helper method and call it in both applicationWillResignActive: and applicationDidEnterBackground:
-(void)saveUserDefaults {
NSLog(#"application did enter background");
NSUserDefaults *storage = [NSUserDefaults standardUserDefaults];
[storage synchronize];
}
Also, make sure that Application does not run in background in your info.plist is set to NO else your app will go to the suspended.
I have registered my app to be able to open HTML files. I would like the app to keep the last HTML file it opened... If I open a new HTML file, I would like it to overwrite the last one opened.
I have attempted to user the Documents directory and its not working for me. I have also tried using NSUserDefaultsto no avail. Here is what i currently have:
-(BOOL)application:(UIApplication *)application handleOpenURL:(NSURL *)url
{
ViewController *vc = (ViewController *)self.window.rootViewController;
[vc loadFileWithUrl:url];
[[NSUserDefaults standardUserDefaults] setURL:[url fileReferenceURL] forKey:#"amd"];
return YES;
}
I kill the application entirely, then - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions is called. Here is how that method looks:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
NSURL *url = [[NSUserDefaults standardUserDefaults] URLForKey:#"amd"];
if (url)
{
ViewController *vc = (ViewController *)self.window.rootViewController;
[vc loadFileWithUrl:url];
}
return YES;
}
url is always nil :(
PS: I have also tried storing the HTML file in its string representation, and I am able to get that to persist in the Documents folder, but when I try to load that string using:
[webView loadHTMLString:htmlString]; the page never loads. I think that has something to do with the Javascript but I'm not too sure.
The user defaults are saved when the app goes to the background, or when you call -synchronize on the user defaults. When developing, you often don't put the app into background, but just kill it through XCode, in which case nothing gets saved. You can save the user defaults by pressing the home button on your iPhone/iPad, and killing the app in XCode 1second later.
Also, you should put a log statement into your first method, like NSLog(#"saving amd to %#", [url fileReferenceURL]); to be sure that it really gets saved!
It seems that you forgot to call
[[NSUserDefaults standardUserDefaults] synchronize];
Docs:
Writes any modifications to the persistent domains to disk and updates
all unmodified persistent domains to what is on disk.
I would like to do some interaction with my server when my app quits at the time of crash due to low memory, memory leaks etc. general crashes. I would like to know, Is there any delegate methods will be called in this scenario, so that i can contact my server quickly and immediately before the app quits due to any crash.
Thank you.
As you explained you need to intimate server you can contact your server immediately before the app quits due to any crash.
in that case you should set exception handler as any exception will occur you will get notify
See how can you do this
write this NSSetUncaughtExceptionHandler (&uncaughtExceptionHandler) line of code in applicationDidFixnishLaunchin Method of Appdelegate Class
-(BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:
(NSDictionary*)launchOptions
{
NSSetUncaughtExceptionHandler(&uncaughtExceptionHandler);
EDIT:
if( [[NSUserDefaults standardUserDefaults] boolForKey:#"isExceptionOccured"])
{
//call sever code here
[[NSUserDefaults standardUserDefaults] setBool:FALSE forKey:#"isExceptionOccured"];
}
//rest of your code
}
void uncaughtExceptionHandler(NSException *exception)
{
NSLog(#"Exception Got %#",[exception description]);
//do what ever you what here
//can save any `bool` so that as aaplication run on immediate next launching of crash
//could intimate any thing
EDIT:
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:#"isExceptionOccured"];
}
Add your own exception handler, to catch the error.
Firstly define the exception method:
void uncaughtExceptionHandler(NSException *exception) {
// You code here, you app will already be unload so you can only see what went wrong.
}
Then tell the app to use your exception handler:
- (void)applicationDidFinishLaunching:(UIApplication *)application {
NSSetUncaughtExceptionHandler(&uncaughtExceptionHandler);
// The rest if you code ....
}
hope it helps you.
So, here is my life story that I can't seem to figure out what's wrong!!!
I have an app that's already in the store and it has a weird issue which it gives out mixed results.
I have two switches in my main view both controls local notification with repeat intervals.
now I've set up the switches right and ready to go. (I guess).
However, when repeat intervals are scheduled they have to be cancelled again when the user decided not to receive notifications. So, I had my switches coded like that:
- (void)applicationDidEnterBackground:(UIApplication *)application
{
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
BOOL switchOn = [userDefaults boolForKey:#"switchO"];
if (switchOn) {
[self Firstnotif];
[self Secondnotif];
[self Thirdnotif];
[self Lastnotif];
}
else { [[UIApplication sharedApplication] cancelAllLocalNotifications]; }
BOOL switchOn2 = [userDefaults boolForKey:#"switchO2"];
if (switchOn2) {
[self Firstnotif];
}
else { [[UIApplication sharedApplication] cancelAllLocalNotifications]; }
}
notice I have used the method Firstnotif twice in both switches.
My issues are:
I used to have only one switch and when they turn the first switch on to receive these four notification, doesn't work with all of the users. so I tell them to switch it off, press home button, open app again switch it on and home button again. It works!!!! why?
Now, since I've added another switch new mixed results are appearing.
the second switch is to only fire first notification with it's repeat interval. Some say it works, some say not. others say when I turn the other one it does and does not. What are they facing here??
My questions,
Am I canceling the repeat intervals in the right way?
What could be wrong with my app? and I'll provide you with more codes if needed.
I appreciate your inputs as I've spent weeks on these issues with no luck.
I solved it!
I replaced the cancellation request under applicationDidBecomeActive method and removed my else
guess what? It worked!!! :)
Im having a ViewController on which on clicking a button, label gets updated. I want is, each time the app opens it should retain its old value. Im able to write every value in NSUserDefault but not getting how to write the value on label before the app loads.
Example:
In first run the label is having the value 5.
In second run the label should contain the same value 5, and if i made any changes that change should be there in third run.
Thanx...
In your AppDelegate, the very first method gets called after launching an App is,
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Override point for customization after application launch.
return YES;
}
and for other cases such as from inactive to active state of your application use,
- (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.
}
I recommend Reading, Apple's iOS application lifecycle documentation.
Here's how to retrieve a stored value from NSUserDefaults and set the value for your label when your ViewController loads:
- (void)viewDidLoad
{
[super viewDidLoad];
self.myLabel.text = [[NSUserDefaults standardUserDefaults] objectForKey:#"mySavedValue"];
}
I'm assuming your button is already hooked up to an action something like this that will save the value to the standardUserDefaults when clicked:
- (IBAction)buttonPressed:(id)sender
{
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:self.myLabel.text forKey:#"mySavedValue"];
[defaults synchronize];
}