I want to show a help overlay when a user runs my application for the first time.
To do this, I'm using the following code indidFinishLaunching:
if(![[[[NSUserDefaults standardUserDefaults] dictionaryRepresentation] allKeys] containsObject:#"IPHONEFIRSTRUN"])
[[NSUserDefaults standardUserDefaults]setBool:TRUE forKey:#"IPHONEFIRSTRUN"];
In the view controller, I have:
if ([[NSUserDefaults standardUserDefaults]boolForKey:#"IPHONEFIRSTRUN"]==TRUE) {
[self HelpOverlayIphone];
[[NSUserDefaults standardUserDefaults]setBool:FALSE forKey:#"IPHONEFIRSTRUN"];
}
However, it shows the overlay on the second use as well. How can I fix this? Any help is appreciated.
Your logic is overly complex. You are setting permanently a user default to indicate something happening one time. Instead, in the view controller see if the value is not set, if it is do your action and set the variable so that the code is NOT run again:
if ( ! [[NSUserDefaults standardUserDefaults]boolForKey:#"IPHONEFIRSTRUNCOMPLETE"] ) {
[self HelpOverlayIphone];
[[NSUserDefaults standardUserDefaults]setBool:YES forKey:#"IPHONEFIRSTRUNCOMPLETE"];
[[NSUserDefaults standardUserDefaults] synchronize];
}
Take out all the code in the app delegate.
Also it's very likely your original code is not working because you are stopping the app from XCode. If you don't use synchronize user default changes will not be saved in that case (normal quitting of the app does eventually save the changes permanently).
Have you registered the defaults you are using at the start of the program? I would suggest rereading the apple documentation for NSUserDefaults here
You need to first make a call to
- (void)registerDefaults:(NSDictionary *)dictionary
which will only set the key if it doesn't already exist. Then check the key for falseness on the the first run and set it at this point.
My guess is the reason your code isn't working is because the dictionary is never actually being saved in any sort of persistent way.
I have this function which fires when the app fires up- it's always been reliable. I have a uniqueNameOfApp which is just some random hash so that it doesn't collide with other apps.
-(void)loadSettings {
NSMutableDictionary *sttngs = [[NSUserDefaults standardUserDefaults]
objectForKey:uniqueNameOfApp];
if(sttngs != nil) {
[userSettings addEntriesFromDictionary:sttngs];
} else {
_appFiredForFirstTime = YES;
}
}
Related
For instance, I want a logon screen in ViewController1 to be displayed if the app is launched for the first time. If the app is not launched for the first time or the user has already logged in, it will just start from ViewController2. How do I implement this?
Try this link: Check for first launch of my application
The best code for this link was done by Omar, in my opinion. But there are a few great answers on there, as well as one answer with good philosophy on the matter. If you want a small explanation on how to run the code and understand it yourself, NSGod has a good code snippet for you.
If that doesn't help, try this link: How to setup first time launch of an iOS App
ALSO, Here's some code I whipped up for you . . .
- (BOOL)isFirstTimeLaunching {
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
if([defaults integerForKey:#"hasRun"] == 0) {
// Do some first-launch code...
[defaults setInteger:1 forKey:#"hasRun"];
[defaults synchronize];
return YES;
}
return NO;
}
Remember, returning 0 is returning true. Returning anything other than 0 is returning false. When the app hasn't been run yet, the key #"hasRun" will be false, and will return 0. Once it's been run, reset it back to 0 to prevent the first-launch code from running again.
I would use NSUserDefaults. In the AppDelegate.m do something like:
if(![[NSUserDefaults standardDefaults] objectForKey:#"shouldLaunchSignUp"]){
//launch your sign up view controller here
//set the user default to yes
[[NSUserDefaults standardDefaults] setBool:YES forKey:#"shouldLanuchSignUp"];
}
check in viewDidLoad and set a boolean flag.
- (BOOL)isFirstTimeLaunching {
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
if([defaults integerForKey:#"hasRun"] == 0) {
// Do some first-launch code...
[defaults setInteger:1 forKey:#"hasRun"];
[defaults synchronize];
return YES;
}
return NO;
}
And in viewDidAppear, on basis of flag present your log in view.
Remember , in viewDidLoad you can't present another view.
I placed a BOOL inside a UIButton. If I click on the Button once, then I want that BOOL to always be saved so I never has to click on button again while in the game. I used the following code but it doesn't work to save. However, the code should work. It's mainly the final line of code that makes the saving not work. What am I doing wrong?
-(void)viewDidLoad{
[[NSUserDefaults standardUserDefaults] setBool:REDguy forKey:#"save"];
if (REDguy==[[NSUserDefaults standardUserDefaults]synchronize]) {
MAN.image=[UIImage imageNamed:#"RED.png"];
}
REDguy=[[NSUserDefaults standardUserDefaults]boolForKey:#"save"];
}
EDIT:
if (REDguy==YES) {
[[NSUserDefaults standardUserDefaults] setBool:REDguy forKey:#"save"];
REDguy =[[NSUserDefaults standardUserDefaults]boolForKey:#"save"];
MAN.image=[UIImage imageNamed:#"RED.png"];
}
This is an assumption since you didn't really clarify what else is going on in your code, but it looks like your bool REDGuy is always going to be be NO on viewDidLoad, and that if condition is likely to always be false since synchronize will probably always be YES (not to mention, that comparison makes no sense and it's rarely necessary to call synchronize since synchronization with user defaults happens on its own).
A simple way to determine if a button has been pressed before is to add the following to your button action method.
- (void)buttonAction:(id)sender {
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:#"save"];
}
- (void)viewDidLoad {
BOOL hasSaved = [[NSUserDefaults standardUserDefaults] boolForKey:#"save"];
if (hasSaved) {
// You can hide your button here if that's what you're going for
[yourButton setHidden:YES]
}
}
Is that what you are trying to accomplish? Determining if a specific button has been tapped before?
I am pretty new to iOS so here's my question. I have a Table View (GuestTableViewController) listing some guests in a party. When I click in a person, I show a new view (GuestInfoViewController) with some info about this attendee. In this view I have switch button, so if I have 3 persons, there will be 3 switches indicating each one of them is coming or not.
Using NSUserDefaults in a IBAction in my GuestInfoViewController I have achieved to save its state (ON/OFF) between views.
The problem is that when I click one switch, all switches change state. How can reference each one of the switches.
Note: I can post images on my storyboard or even some code if needed.
Thank you so very much!
#implementation GuestInfoViewController
#synthesize nom,cognoms,foto;
#synthesize setNom,setCognoms,setFoto;
#synthesize mySwitch;
- (void)viewDidLoad
{
[super viewDidLoad];
nom.text = setNom;
cognoms.text = setCognoms;
[foto setImage:[UIImage imageNamed:setFoto]];
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
if ([defaults objectForKey:#"SwitchState"])
self.mySwitch.on = [defaults boolForKey:#"SwitchState"];
}
- (IBAction)switch:(id)sender {
if(mySwitch.on){
NSLog(#"Switch is ON");
}
if(!mySwitch.on){
NSLog(#"Switch is OFF");
}
}
- (IBAction)saveSwitchState:(id)sender
{
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
if ([self.mySwitch isOn])
[defaults setBool:YES forKey:#"SwitchState"];
else
[defaults setBool:NO forKey:#"SwitchState"];
}
#end
Your code uses the same key for all the attendees - that is what you should take care of.
Since you obviously set the name and surname for each person (and if we presume that two attendees don't have the same name) you could use this to your advantage.
Change all the #"SwitchState" references
to something like
[NSString stringWithFormat:#"SwitchState_%#_%#",setNom,setCognoms]
This would effectively save the state of the switches for each attendee separately.
Using you're line of thinking, you would need to store 3 keys in NSUserDefaults, one for each person. It would be a mess to use it, for instance, if you have 1000 persons.
I believe the proper way to implement this is by using a Delegate on your GuestInfoViewController.
Here's what I would do:
GuestTableViewController have a list of Person objects, Person object have a BOOL for selected or not.
GuestInfoViewController, reads the BOOL value to show the switch, if the value is changed it fires the delegate and updated the list in GuestTableViewController.
This way, everything is updated and you have all information correct. If you need help doing the delegate, you can find a million examples on Stackoverflow. Or ask and I'll elaborate.
** edit **
When dealing with a simple Person object, you don't even need a delegate, its simplier. Check the project in attach: http://www.brunotereso.net/temp/DelegateProject.zip (Please note this is just a piece of code I've put up to show you how to do it. If you implement something like this, have a look on cellForRowAtIndexPath and use reusable cells)
Say a user logs in to a certain service in my app. How do I "remember" that the user did this, so it will no longer prompt him/her to do this?
For example, every time the app launches I want it to do something with the user account if they are logged in otherwise, don't do anything. The user logs in from an optional login window they can invoke. Once they login, I'd like to (conceptually at least) set a variable so I can be like if (userIsLoggedIn) { ... } and do things accordingly.
Is this NSUserDefaults? CoreData?
You can easily store it in NSUserDefaults.
// init
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
// set the default value
[defaults setObject:#"YES" forKey:#"loggedIn"];
// read value
NSString *loggedIn = [defaults objectForKey:#"loggedIn"]
Update:
Set with BOOL:
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:#"loggedIn"];
read the value:
if(![[NSUserDefaults standardUserDefaults] boolForKey:#"loggedIn"]) {
// go to login screen
} else {
// go to main screen
}
NSUserDefaults exposes your app to security risks if you are using login credentials.
I would recommend using NSKeyedArchiver.
This site has a fantastic simple explanation on how to implement it!
http://haoxiang.org/2011/11/ios-how-to-save-and-load-a-custom-object/
Apple API Doc just for future reference.
https://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/NSKeyedArchiver_Class/Reference/Reference.html
As the other answers have pointed out NSUserDefaults is useful if you are storing simple non private state.
I generally like to wrap this up with a category so that I can interact with user defaults easier and not have to worry about keys etc.
An example would look like this category
NSUserDefaults+PASAuthorisation.h
#interface NSUserDefaults (PASAuthorisation)
#property (nonatomic, assign, getter=pas_isLoggedIn) BOOL pas_loggedIn;
#end
NSUserDefaults+PASAuthorisation.m
#import "NSUserDefaults+PASAuthorisation.h"
static NSString * const PASLogginInKey = #"PASLogginInKey";
#implementation NSUserDefaults (PASAuthorisation)
- (BOOL)pas_isLoggedIn;
{
return [self boolForKey:PASLogginInKey];
}
- (void)setPas_loggedIn:(BOOL)pas_loggedIn;
{
[self setBool:pas_loggedIn forKey:PASLogginInKey];
}
#end
[[NSUserDefaults standardUserDefaults] setObject:#YES forKey:#"isLoggedIn"];
Valid comments: you can also use setBool:forKey with the same result. Calling synchronize right after this assignment also does not hurt (although it is probably not critical).
Core Data isn't really appropriate for small, unique pieces of data like this.
NSUserDefaults is the right place for small things like this, but if those small things are supposed to be secret -- like login details -- the Keychain is better.
I am using NSUSerDefaults to store a couple strings and integers for my application. Whenever a view is opened, the string is loaded slower than the view so you see a glitch. For example, I save the selectedSegmentIndex and then read it in viewDidAppear and for a quick moment when the view is called, no segment is selected, then the right one selects. How do you make it so there is no time gap between the view being opened and the setting be read?
- (void)viewDidLoad
{
[super viewDidLoad];
int segmentIndex = [[NSUserDefaults standardUserDefaults] integerForKey:#"selectedIndex"];
unitSegmentControl.selectedSegmentIndex = segmentIndex;
BOOL location = [[NSUserDefaults standardUserDefaults] boolForKey:#"locationManager"];
[gpsSwitch setOn:location animated:NO];
deviceID.text = [[NSUserDefaults standardUserDefaults] stringForKey:#"DeviceID"];
}
- (IBAction)changeSeg:(id)sender {
if (unitSegmentControl.selectedSegmentIndex == 0) {
[[NSUserDefaults standardUserDefaults] setObject:#"http://98.246.50.81/firecom/xml/units/E01.xml" forKey:#"parserURL"];
[[NSUserDefaults standardUserDefaults] setObject:#"Hillsboro Main" forKey:#"selectedStation"];
[[NSUserDefaults standardUserDefaults] setObject:#"Hillsboro Fire & Rescue" forKey:#"selectedDepartment"];
}
if (unitSegmentControl.selectedSegmentIndex == 1) {
[[NSUserDefaults standardUserDefaults] setObject:#"http://98.246.50.81/firecom/xml/units/E02.xml" forKey:#"parserURL"];
[[NSUserDefaults standardUserDefaults] setObject:#"Hillsboro Witch Hazel" forKey:#"selectedStation"];
[[NSUserDefaults standardUserDefaults] setObject:#"Hillsboro Fire & Rescue" forKey:#"selectedDepartment"];
}
[[NSUserDefaults standardUserDefaults] setInteger:unitSegmentControl.selectedSegmentIndex forKey:#"selectedIndex"];
[[NSUserDefaults standardUserDefaults] synchronize];
}
The defaults are not slow, you’re just loading the data too late. The standard place to populate views is in -viewDidLoad or -viewWillAppear in the view’s controller. Both will update the view soon enough to avoid visual glitches. If any of the two doesn’t work for you, here’s some tips to find the reason:
Try to set the selected index to a hard-wired number. This will tell you if the problem is in the defaults or (much more likely) in the -setSelectedSegmentIndex call.
Move the UI population code to -viewWillAppear. That’s the latest moment to update the UI before it’s displayed.
Use NSParameterAssert to make sure unitSegmentControl is not nil.
Make sure the index read back from the defaults is the expected number. Generally, it’s best to pull the defaults keys into constants. That way you can’t bump into simple typo bugs:
static NSString *const SelectedSegmentKey = #"selectedSegment";
If everything else fails, use a custom UISegmentControl subclass for your unitSegmentControl and place a breakpoint into -setSelectedSegmentIndex to see who else might be calling it.