How to see if a NSUserdefaults is nil or not? - ios

Well I dont know if my title is well drafted but I will try to explain whats my problem, I want to save a NSDate for an IndexPath in NSUserDefaults, this happens when viewWillDisappear but its crashing, its saving correctly because when I reopen the DatePicker loads the date I want but still crash when saving a date at the UserDefaults
So heres my code so you can see whats going on....
I read if the NSUserDefaults is nil or not so I can load the DatePicker:
NSArray *indexParams = [self.userdefaults objectForKey:#"indexpath"];
NSIndexPath *myIndexPath = [NSIndexPath indexPathForRow:indexParams[1]
inSection:indexParams[0]];
self.notificationDate = [self.userdefaults objectForKey:#"date"];
NSDate *date = [self.userdefaults objectForKey:[NSString stringWithFormat:#"%d", myIndexPath.row]];
if(date){
[self.NotSwith setOn:YES];
self.DatePicker.date = [self.userdefaults objectForKey:[NSString stringWithFormat:#"%d", myIndexPath.row]];
}else{
[self.NotSwith setOn:NO];
}
When I want to save the date in viewWillDisappear its when the crash happens:
NSArray *indexParams = [self.userdefaults objectForKey:#"indexpath"];
NSIndexPath *myIndexPath = [NSIndexPath indexPathForRow:indexParams[1]
inSection:indexParams[0]];
NSDate *date = [self.userdefaults objectForKey:[NSString stringWithFormat:#"%d", myIndexPath.row]];
if(date){
// [self.userdefaults synchronize];
}
else{
[self.userdefaults setObject:self.DatePicker.date forKey:[NSString stringWithFormat:#"%d", myIndexPath.row]];
[self.userdefaults synchronize];
[[UIApplication sharedApplication] scheduleLocalNotification:local];
}
But the info its saved successfully and the date picker loads the date in relaunch.
crash log:
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[__NSPlaceholderArray initWithObjects:count:]: attempt to insert nil object from objects[0]'
So hope I explained well, Thanks!
Setting the indexpath:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
NSNumber *section = [NSNumber numberWithInt:indexPath.section];
NSNumber *rows = [NSNumber numberWithInt:indexPath.row];
[self.userdefaults setObject:#[section, rows] forKey:#"indexpath"];
}

Sounds like you want to save both the selected index path and a date. Saving a date in NSUserDefaults is easy.
// no need to keep a property on self for user defaults. you don't need to keep
// that around. just a stack variable will work.
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSDate *date = [NSDate date];
[defaults setObject:date forKey:#"myDate"];
[defaults synchronize];
Get it back later on this way:
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSDate *date = [defaults objectForKey:#"myDate"];
// if nothing has been stored using that key then objectForKey will return nil
if (date) {
// it's there!
} else {
// it's not there
}
Storing an index path is a little more tricky, but this will work:
// wrap your row and section in NSNumbers, wrap those in an array for brevity
NSNumber *section = [NSNumber numberWithInt:myIndexPath.section];
NSNumber *row = [NSNumber numberWithInt:myIndexPath.row];
[defaults setObject:#[section, row] forKey:#"myIndexPath"]; // then synchronize
// naturally, when you get it later, you can check for nil again, and,
// if it's not nil, to rebuild the index path...
NSArray *indexParams = [defaults objectForKey:#"myIndexPath"];
NSIndexPath *myIndexPath = [NSIndexPath indexPathForRow:indexParams[1]
inSection:indexParams[0]];
The key is what isn't in this answer: no NSData, no NSKeyedArchiver, no string manipulation to build an index path representation. Best of luck.

Related

How can I save text data in an iOS application?

Using my application, the user will take a reading on my application, which will yield a string of text in this format: Reading, Date/Time, Gender, Age. Essentially, what I'm doing is taking this string and storing it in a table view in a different storyboard. Then, every time the application is closed and reopened, the saved values should remain. The action of saving the string should be triggered by pressing a save button. How can this be implemented? Sample code is appreciated.
The easiest solution is to use NSUserDefaults:
// Add a string & save permanently
NSMutableArray *savedStrings = [NSMutableArray arrayWithArray:[[NSUserDefaults standardUserDefaults] objectForKey:#"SavedStrings"]];
NSString *sampleString = #"Reading, 4 Feb 2017, 12:00, male, 30 years";
[savedStrings addObject:sampleString];
[[NSUserDefaults standardUserDefaults] setObject:savedStrings forKey:#"SavedString"];
[[NSUserDefaults standardUserDefaults] synchronize];
// At launch, get saved strings - use to populate the tableview
NSMutableArray *savedStrings = [NSMutableArray arrayWithArray:[[NSUserDefaults standardUserDefaults] objectForKey:#"SavedStrings"]];
I think this will help you...
//In firstViewController.m
// Create strings to store the text info
NSString *firstName = [self.firstNameTF text];
NSString *lastName = [self.lastNameTF text];
NSString *age = [self.ageTF text] ;
[defaults synchronize];
// Store the data with key.
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:firstName forKey:#"firstName"];
[defaults setObject:lastName forKey:#"lastname"];
[defaults setObject:age forKey:#"age"];
//In requiredViewController.m write this code.
// Get the stored data before the view loads
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSString *firstName = [defaults objectForKey:#"firstName"];
NSString *lastName = [defaults objectForKey:#"lastname"];
NSString *ageString = [defaults objectForKey:#"age"];
// Set saved data in UIElements.
self.firstNameLabel.text = firstName;
self.lastNameLabel.text = lastName;
self.ageLabel.text = age;

NSUserDefaults save and reload while using app but reset when app is restarted

I have a method to increment a counter if it is the first time a user has been messaged on the particular day. Code below:
//here we decide if to increment it or not
-(BOOL)canIncrementCountForUser: (NSString *)user {
//erase the dictionary if it's a new day
[self flushDictionaryIfNeeded];
//load up a dictionary
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSMutableDictionary *dictionary = [defaults objectForKey:#"uniqueSentToday"];
NSLog(#"%#", [dictionary allKeys]);
//if empty it's a yes
if([dictionary count]==0){
NSLog(#"empty dictionary");
NSLog(#"First message for %# today!",user);
NSDate *now = [NSDate date];
[dictionary setObject:now forKey:user]; //do I need to set it back again?
[defaults synchronize];
return YES;
}
//if it's not empty it's only a yes if the key doesn't exist
else {
//not in dict so unique
if(![dictionary objectForKey:user]){
NSLog(#"First message for %# today!",user);
NSDate *now = [NSDate date];
[dictionary setObject:now forKey:user]; //do I need to set it back again?
[defaults synchronize];
return YES;
}
else {
NSLog(#"Already messaged %# today!",user);
return NO;
}
}
}
-(void)flushDictionaryIfNeeded{
//set dictionary
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSMutableDictionary *dictionary = [defaults objectForKey:#"uniqueSentToday"];
if([dictionary count]>0) {
//get any date
NSDate *aDate = nil;
NSArray *values = [dictionary allValues];
aDate = [values objectAtIndex:0];
NSDateComponents *otherDay = [[NSCalendar currentCalendar] components:NSCalendarUnitEra|NSCalendarUnitYear|NSCalendarUnitMonth|NSCalendarUnitDay fromDate:aDate];
NSDateComponents *today = [[NSCalendar currentCalendar] components:NSCalendarUnitEra|NSCalendarUnitYear|NSCalendarUnitMonth|NSCalendarUnitDay fromDate:[NSDate date]];
if([today day] == [otherDay day] &&
[today month] == [otherDay month] &&
[today year] == [otherDay year] &&
[today era] == [otherDay era]) {
NSLog(#"Don't flush");
}
else {
NSLog(#"It's a new day! erase dictionary!");
[dictionary removeAllObjects];
[defaults synchronize];
}
}
}
If a user is messaged a NSDate object will be created with the user's username as the key. If a key doesn't exist the counter can be incremented and the key is added, if it does exist the method returns false. I also have a method to erase all the contents if it's a new day. The code is all working fine and it seems like the NSMutableDictionary is being saved to the application fine when the program is being used but when I restart the app the dictionary will be empty. Can someone give me a pointer on why this might be happening? Thanks
You modified the dictionary but never set back the defaults... You need:
[defaults setObject:dictionary forKey:#"uniqueSentToday"];
to save the defaults.
You need to store the dictionary back in the user defaults as the dictionary you receive is immutable. Although you set it as mutable, changes will not be reflected in the user defaults original stored dictionary.

Saving a dynamic tableview's textfields

I keep posting about this same issue but I can't seem to get the answer and I've easily spent 30+ hours trying to figure it out. I will keep it as simple as possible. I have a dynamic tableview that adds custom cells when the "+" button is pressed. Each cell has 6 textFields. My problem now is that I save the textFields for one cell and it loads the same thing for every other cell. (by the way please just disregard the dynamic tableview reuse function)
Here is my code
-(UITableViewCell *) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellIdentifier = #"expandingCell";
ExpandingCell *cell = (ExpandingCell *)[tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (cell == nil)
{
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"ExpandingCell" owner:self options:nil];
cell = [nib objectAtIndex:0];
cell.autoresizingMask = UIViewAutoresizingFlexibleHeight;
cell.clipsToBounds = YES;
}
return cell;
}
Here is my code for the cells class: ExpandingCell.m
- (IBAction)Save:(id)sender
{
NSString *saveString = Date.text;
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:saveString forKey:#"saveString"];
[defaults synchronize];
NSString *saveString1 = Course.text;
NSUserDefaults *defaults1 = [NSUserDefaults standardUserDefaults];
[defaults1 setObject:saveString1 forKey:#"saveString1"];
[defaults1 synchronize];
NSString *saveString2 = Score.text;
NSUserDefaults *defaults2 = [NSUserDefaults standardUserDefaults];
[defaults2 setObject:saveString2 forKey:#"saveString2"];
[defaults2 synchronize];
NSString *saveString3 = Par.text;
NSUserDefaults *defaults3 = [NSUserDefaults standardUserDefaults];
[defaults3 setObject:saveString3 forKey:#"saveString3"];
[defaults3 synchronize];
NSString *saveString4 = Putts.text;
NSUserDefaults *defaults4 = [NSUserDefaults standardUserDefaults];
[defaults4 setObject:saveString4 forKey:#"saveString4"];
[defaults4 synchronize];
NSString *saveString5 = GIR.text;
NSUserDefaults *defaults5 = [NSUserDefaults standardUserDefaults];
[defaults5 setObject:saveString5 forKey:#"saveString5"];
[defaults5 synchronize];
}
- (IBAction)Load:(id)sender
{
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSString *loadString = [defaults objectForKey:#"saveString"];
[Date setText:loadString];
[DateLabel setText:loadString];
NSUserDefaults *defaults1 = [NSUserDefaults standardUserDefaults];
NSString *loadString1 = [defaults1 objectForKey:#"saveString1"];
[Course setText:loadString1];
[CourseLabel setText:loadString1];
NSUserDefaults *defaults2 = [NSUserDefaults standardUserDefaults];
NSString *loadString2 = [defaults2 objectForKey:#"saveString2"];
[Score setText:loadString2];
NSUserDefaults *defaults3 = [NSUserDefaults standardUserDefaults];
NSString *loadString3 = [defaults3 objectForKey:#"saveString3"];
[Par setText:loadString3];
NSUserDefaults *defaults4 = [NSUserDefaults standardUserDefaults];
NSString *loadString4 = [defaults4 objectForKey:#"saveString4"];
[Putts setText:loadString4];
NSUserDefaults *defaults5 = [NSUserDefaults standardUserDefaults];
NSString *loadString5 = [defaults5 objectForKey:#"saveString5"];
[GIR setText:loadString5];
}
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self)
{
// Initialization code
}
return self;
}
- (void)setSelected:(BOOL)selected animated:(BOOL)animated
{
[super setSelected:selected animated:animated];
// Configure the view for the selected state
}
Please someone help its for a school project and I've desperately been trying to finish it before the deadline but I'm just totally lost on how to do this
In your ExpandingCell implement prepareForReuse method and clear the text fields.
Do the cells get reset immediately? Because if not, it seems like Zaph is correct that they all seem to be pointing to the same data. What you should do is create some kind of array or plist where you can store data for each cell rather than allocate and initialize data pointing to the same defaults. There is no way, afaik, that your cells can differentiate. Everytime you perform a load/save action, your data is written to the same place.
NSString *saveString = Date.text;
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:saveString forKey:#"saveString"];
[defaults synchronize];
^ You overwrite the value for key saveString each time, thats why when it is reloaded, you load the saved value. What you can do is do something along the lines of:
[defaults setObject:saveString forKey:[NSString stringWithFormat:#"saveString%ld",(long)indexPath.row]];
This will ensure that a set of unique keys exist for each row, you can change out the row value for any other method of uniquely identifying a cell. perhaps you can try this:
[defaults setObject:saveString forKey:[NSString stringWithFormat:#"saveString1%#",nameOfGolfer]];
What would be better is to create an NSDictionary, and inserting an organized array of the date, course, putt # and all that jazz.
This way, each key for the NSDictionary would contain an array, and the key for each array would be a golfer name or course name or something. You just need to add one level higher of organizing your data and it will work.
EDIT1:
If you did that, then you would only have one object for the key saveString. A little noob to noob crashcourse of dictionaries and arrays. Basically NSUserDefault is really just an NSDictionary that can assign ONLY ONE (NS) object/value/number/anything to ONE key. A normal NSDictionary can not have objects as properties, but can have multiple values for ONE key. So you could add a NSString objet and then reference it with the key #"stringCatogory1", but there can only be one object for that key.
I think the NSUserDefaults is just a tool that Apple provided by combining an array and dictionary to let people make data persistent without creating their own custom framework for trying to save volatile data manually. You could basically just create a plist and manage it with an nsdictionary object everytime you wanted to change a value, essentially creating your own custom nsuserdefaults.
for example
[defaults setObject:firstName forKey:#"firstName"];
[defaults setObject:lastName forKey:#"lastname"];
[defaults synchronize]; //overwrites previous values saved from previous synch

NSUserDefaults if data equal to something then NSLog

I created a simple app when you click on a button it saves first click with 1 second with 1 and so. So, I tried to check if the first click equal to 1 then NSLog but it didn't worked.
Here's my code:
-(IBAction)add:(id)sender
{
//Once the user click on the button it adds 1
static int num = 0;
num+=1;
//Save the number for key "saved and the number" ex: if number is 1 so for key is "saved 1"
NSString *dataa = [NSString stringWithFormat:#"%i", num];
NSString *fkey = [NSString stringWithFormat:#"saved %i", num];
NSUserDefaults *def = [NSUserDefaults standardUserDefaults];
[def setObject:dataa forKey:fkey];
[def synchronize];
}
-(IBAction)load:(id)sender
{
//load the data forkey "saved 1" wich will be '1'
NSUserDefaults *def = [NSUserDefaults standardUserDefaults];
NSString *loadd = [def objectForKey:#"saved 1"];
NSLog(#"%#",loadd);
//and here I failed, here I want to check if [def objectForKey:#"saved 1"] == '1'
//then NSLog#"Yes" but it NSLogs "NOO"
NSString *no = #"1";
if(loadd == no){
NSLog(#"YES");
}else{
NSLog(#"NOO");
}
}
So what is the issue here?
Thanks in Advance
use [loadd isEqualToString:no] instead, that compares strings.
here is the documentation link

How to add to a saved NSUserDefault?

I am making a game in Xcode which in includes a scoring system in each level. Here I have some code that gets an NSString (passedValue1) by using a delegate.
Then i add the code to receive the value in my viewDidLoad and display my value in a UILabel
-(void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib
// Do any additional setup after loading the view, typically from a nib
NSString *key=#"labelKey";
if(passedValue1){
NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
NSObject * object = [prefs valueForKey:key];
if(object != nil){
NSUserDefaults *defults = [NSUserDefaults standardUserDefaults];
[defults setValue:passedValue1 forKey:key];
[defults synchronize];
}
else{
NSUserDefaults *defults = [NSUserDefaults standardUserDefaults];
NSInteger readScore=[[[NSUserDefaults standardUserDefaults] valueForKey:key] integerValue];
NSInteger newValue=readScore+[passedValue1 integerValue];
[defults setValue:[NSString stringWithFormat:#"%d",newValue] forKey:key];
[defults synchronize];
}
}
label.text = [[NSUserDefaults standardUserDefaults]objectForKey:key];
}
Once I have displayed the value I then save it into a label using a NSUserDefault. However, once I have replayed my game and have another score value I would like to add the new passedValue1 value to the currently saved value...
For example:
say I play my level and I get the score value of 10. The value is then saved and I replay my level. I would then like to take the saved value and add it to the value i just scored. So thats say the second value I have scored is 20. I would then like my code to add them together and give me a value of 30.
can anyone help because the code I'm using does not correctly do the function that I want to do which is add the previous and the currently passed value.
What is wrong with my code??
Thanks in advance.
You shouldn't use valueForKey: for this (it's not what you might think it is, see Key-Value Coding). Instead, you should use objectForKey: or in your case integerForKey:.
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSInteger value = [defaults integerForKey:key];
value += [passedValue1 integerValue];
[defaults setInteger:value forKey:key];
BOOL success = [defaults synchronize];
It seems that you got your if condition wrong, it should be
if (object == nil) { // <-- you have object != nil here !
// save new value
} else {
// read old value, add something, save new value
}

Resources