I have a simple messaging app, and I'm keeping a dictionary of BOOL:user in the NSUserDefaults which simply represents if something "new" has happened in that conversation. {YES:12343} for example, means there is a new message with user 123432, otherwise NO.
When users interact with each other, I update that dictionary and my view accordingly. And when I leave the app, save the dictionary to the NSUserDefaults. When I come back, I simply load it. Everything works smoothly, expect one thing.
When I tap on a conversation to open it, I set that boolean to NO (because I assume the user has read the message) and save that modified dictionary into the NSUserDefaults again.
Debug shows the dictionary is up to date when saved, but when I tap the "back" button, the view reloads the dictionary from the NSUserDefaults and that dictionary is NOT up to date. So my view is showing the conversation as unread, obviously.
Now the tricky parts comes into play. If I do it again, (sometimes once, sometimes twice), the dictionary will eventually show the conversation as read (because the dictionary will finally be up to date).
This tells me some things :
The dictionary is readable and everything is set "as it should/when it should"
What I get from the NSUserDefaults isn't updated quick enough/at the right time.
What I fail to understand is : when should I save that dictionary and how? I'm loading it in viewWillAppear, and saving it in didSelect. Isn't that the right thing to do ?
Some code :
My didSelect :
pushDict is an NSMutableDictionary object and is never nil at that point
if (pushDict != nil){
[pushDict setObject:[NSNumber numberWithBool:NO] forKey:_friendship.objectId];
[[NSUserDefaults standardUserDefaults]setObject:pushDict forKey:#"pushDict"];
}
And the dictionary loading :
if([[NSUserDefaults standardUserDefaults]dictionaryForKey:#"pushDict"]){
pushDict = [[NSMutableDictionary alloc]initWithDictionary:[[NSUserDefaults standardUserDefaults]dictionaryForKey:#"pushDict"]];
}else{
pushDictFeel = [NSMutableDictionary alloc]init];
}
First of all NSUserDefaults shouldn't be the place where you save information like this. Try to setup a good data model for this. However your error could occure because you are missing this line:
[[NSUserDefaults standardUserDefaults] synchronize];
From your code I can see that you are trying to save a NSMutableDictionary. This will not work since the returned object from the NSUserDefaults is immutable. Have a look at this:
NSMutableDictionary in NSUserDefaults
You need to call synchronize for [NSUserDefaults standardUserDefaults]
if (pushDict != nil){
[pushDict setObject:[NSNumber numberWithBool:NO] forKey:_friendship.objectId];
[[NSUserDefaults standardUserDefaults]setObject:pushDict forKey:#"pushDict"];
[[NSUserDefaults standardUserDefaults] synchronize];
}
Related
I am doing a project in objective-c and I have to store some values in userDefaults. I am already did the same thing in the same project. It was working fine but now I am trying to add some values in userDefaults it showing error while fetch any data from userDefaults. I am adding the datas as,
NSString * totalBitValue = [NSString stringWithFormat:#"%.6f Bit",
totalValue /
[[[NSUserDefaults standardUserDefaults] objectForKey:#"Bit"] floatValue]];
[[NSUserDefaults standardUserDefaults]setObject:totalBitValue forKey:#"totalBTCValue"];
[[NSUserDefaults standardUserDefaults]setFloat:totalUSDValue forKey:#"totalUsedValue"];
[[NSUserDefaults standardUserDefaults] synchronize];
and the error as ,
error: Trying to put the stack in unreadable memory at: 0x7ffeeafe2af0
I got the error in the following line,
if( [[NSUserDefaults standardUserDefaults] objectForKey:#"CoinValue"] != nil) {
[[self ValueLabel] setText: [NSString stringWithFormat:#"%#", [[NSUserDefaults standardUserDefaults] objectForKey:#"CoinValue"] ]];
}
Anyone can help me.Thanks in advance
The reason for your issue is because your code gets recursion.
As I understand, you used NSNotificationCenter to observer key NSUserDefaultsDidChangeNotification. So whenever NSUserDefaults is updated, it will trigger defaultsChanged method. But inside it, you continue to change NSUserDefaults, set new value for totalBTCValue and totalUSDValue (as I guess) which makes defaultsChanged be called again and leads to recursion.
To fix the issue, you shouldn't update NSUserDefaults inside defaultsChanged method
I set the NSURLSessionDownloadTask in NSMutableDictionary. Then I want to save this dictionary in NSUserDefaults.
I know that I can't save custom object in NSUserDefaults and, after searching, I've found this answer: How to store custom objects in NSUserDefaults, but I couldn't implement to my code.
Here is part of my code:
[self.downloadTasksDictionary setObject:dlTask forKey:[NSString stringWithFormat:#"%#",customId]];
[[NSUserDefaults standardUserDefaults] setObject:self.downloadTasksDictionary forKey:#"DownloadTaskDict"];
[[NSUserDefaults standardUserDefaults] synchronize];
Is there any way to save this NSMutableDictionary in NSUserDefaults?
Thanks.
You need to use NSKeyArchiver for this. Take a look at this SO post: Save custom objects into NSUserDefaults and Why does NSUserDefaults fail to save NSMutableDictionary?
But, why not saving request descriptor instead of downloadTask object?
In my project I have a NetworkManager which creates a NetworkTask from a NetworkRequest which is a struct. This struct can be created from a json. It works really well.
I am creating this application which consists of the users score. When they exit out of the app, there score will be saved. I am doing that through NSUserDefaults which works fine. The problem is that the users score can be manipulated in other views. For example, they can use their score to buy stuff, etc. To do this I am saving the score in NSUserDefaults and then just retrieving that saved data in the next view. The problem is that the data is not being saved for some reason. I have a timer that runs every second that saves the score and I save it using an IBAction when I click the button to go to the next view. Here is what I use to initialize the variable:
score = [[[NSUserDefaults standardUserDefaults] objectForKey:#"score"] longLongValue];
And this is to save it:
[[NSUserDefaults standardUserDefaults] setObject:#(score) forKey:#"score"];
I am using a long long variable which is why I have it as an object as opposed to integer. I do not understand why the values are getting changed as I change views. Anytime I manipulate the score value, I save it. At the top of each view I am declaring it. I realize that other people have posted similar questions, and I have viewed them all. Those responses did not solve my problem which is why I asked on here. Synchronize
[[NSUserDefaults standardUserDefaults] synchronize];
has not worked for me either.
Only thing you are missing is, synchronize you are not telling NSUserDefault to write data on disc.
// *** Get your score value ***
score = [[[NSUserDefaults standardUserDefaults] objectForKey:#"score"] longLongValue];
// *** manipulate it if needed ***
score+=100;
// *** save it back to `NSUserDefaults` ***
[[NSUserDefaults standardUserDefaults] setObject:#(score) forKey:#"score"];
// *** Synchronize your updates to `NSUserDefaults` by calling it, it immediately write data to disc ***
[[NSUserDefaults standardUserDefaults] synchronize];
For more details about NSUserDefaults read Apple Documentation
Use this. This will save the value
[[NSUserDefaults standardUserDefaults] setObject:#(score) forKey:#"score"];
[[NSUserDefaults standardUserDefaults] synchronize];
I am new to iOS development and have the following problem.
EXPLANATION:
I have an app which consists of several mini games. Each mini game is available through in app purchase. However the mini game availability is saved in a BOOL variable like this:
_miniGamesArray = [[NSMutableArray alloc] init];
NSMutableDictionary *mutDictMg0 = [NSMutableDictionary dictionaryWithCapacity:3];
[mutDictMg0 setValue:[self.gameAvailabilityMutableArray objectAtIndex:0] forKey:#"GameAvailable"];
[_miniGamesArray addObject:mutDictMg0];
PROBLEM:
Each time I start the app the game availability is checked from the self.gameAvailabilityMutableArray which is set to:
- (NSMutableArray *)gameAvailabilityMutableArray
{
if (!_gameAvailabilityMutableArray)
{
//[_gameAvailabilityMutableArray addObjectsFromArray:#[#1,#0,#0,#0 ,#0,#0,#0,#0]];
_gameAvailabilityMutableArray = [[NSMutableArray alloc] initWithArray:#[#1,#1,#1,#1 ,#1,#1,#1,#1]];
}
return _gameAvailabilityMutableArray;
}
When the customer buys a mini game I want the array to be set to (example):
#[#1,#1,#0,#0 ,#0,#0,#0,#0]]
TRIED SOLUTIONS:
I tried to implement the array by calling the iTunes server and writing the data. However, the time to recieve the request is greater then the app loading time. The second problem arrises if there is no internet connection, then the app crashes.
I also tried using .plist files. I don't know why but writing to the plist file doesn't change it's contest all the time! Some times it works , sometimes it doesn't... Sometimes the app loads the values correctly sometimes it mixes them with the last values.
QUESTION:
Is there a way to store permanent app data which is being checked when the app loads beside plists?
Thank you for your time.
save your data in NSUserDefaults.then use below conditions for app start first time or handle another conditions.
BOOL iSFirstTime = [[NSUserDefaults standardUserDefaults] boolForKey:#"AppStartFirstTime"];
if (!iSFirstTime) {
NSLog(#"Application start first time");
[[NSUserDefaults standardUserDefaults] setValue:#"ImgDownloaded" forKey:#"ProductIDDef"];
[[NSUserDefaults standardUserDefaults] synchronize];
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:#"AppStartFirstTime"];
}else{
// NSLog(#"we are here");
if ([[[NSUserDefaults standardUserDefaults] valueForKey:#"ProductIDDef"] isEqualToString:#"ImgDownloaded"]) {
NSLog(#"All transaction and Image Downloading has been perform successfully");
}else{
//If any network problem or any thing else then handle here.
}
}
}
You can save the data in NSUserDefaults using the following code:
[[NSUserDefaults standardUserDefaults] setObject:_gameAvailabilityMutableArray forKey:#"gameArray"];
[[NSUserDefaults standardUserDefaults] synchronise];
and you can retrieve the array using
_gameAvailabilityMutableArray = [[NSUserDefaults standardUserDefaults] objectForKey:#"gameArray"];
I am using these methods to save and get data from NSUserDefaults. But NSUserDefaults is being cleared automatically in device after random period of time. I checked the project-
I am not saving any nil or empty string using saveUserName method.
Not using removeObjectForKey, removePersistentDomainForName to reset NSUserDefault.
Code...
+(NSString *) getUserName{
return [[NSUserDefaults standardUserDefaults] objectForKey:kAccUserNameKey];
}
+(void)saveUserName:(NSString *) value {
[[NSUserDefaults standardUserDefaults] setObject:value forKey:kAccUserNameKey];
[[NSUserDefaults standardUserDefaults] synchronize];
}
After saving data using saveUserName, I get data properly using getUserName, even after restarting app. But It has been reported that data is being lost sometimes.(getUserName is called from didFinishLaunchingWithOptions and applicationWillEnterForeground) Why NSUserDefault is resetting? any idea??
From documentation
(NSUserDefaults *)standardUserDefaults
Description
Returns the shared defaults object. If the shared defaults object does not exist yet, it is created with a search list containing the names of the following domains, in this order:
NSArgumentDomain
A domain identified by the application’s bundle identifier
NSGlobalDomain
NSRegistrationDomain
Is that means, sometimes shared object does not exist yet and its returning nil? whats the solution?
Did you create NSRegistrationDomain domain for your data dictionary via -registerDefaults: ?
As documentation says:
At launch time, an app should register default values for any
preferences that it expects to be present and valid.
You can do it like this, to make NSUSerDefaults work properly:
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
NSDictionary * defaultsDictionary = #{kAccUserNameKey : #"default name"}
[[NSUserDefaults standardUserDefaults] registerDefaults:defaultsDictionary];
return YES;
}
In the defaultsDictionary you should specify all data keys you will use in app with their default values.