iOS how to store data even if the app is closed - ios

I am doing an iOS app. The goal is to send suggestions from user to improve the service I propose.
The app is a native app. I want to create a global NSMutableArray where I store all the suggestions. The problem is my device can be not connected to internet.
There is two cases:
If the device is connected when the user send his suggestion, it is send.
If not, the suggestion is saved in my NSMutableArray and send later.
Is it possible to create a global variable which store all my suggestions ? If my app is crashed/closed this variable can't be dealloc/reset.
Is a singleton enough to do this ?

first check for internet status of your device.if status is connected then send the array.if status in not connected then save the array into NSUserDefaults.To save suggestions array
NSUserDefaults * defaults = [NSUserDefaults standardUserDefaults];
NSMutableArray * suggestions = [[NSMutableArray alloc] init];
[defaults setObject: suggestions forKey:#"suggestions"];
to Access those suggestions
NSUserDefaults * defaults = [NSUserDefaults standardUserDefaults];
NSMutableArray * suggestions = [[NSMutableArray alloc]initWithArray:[defaultsobjectForKey:#"suggestions"]];

A singleton is not enough because you are going to lose all your data if your app is killed or crashes.
If there are only a few suggestions and they are short, the easiest approach would be to save them in your UserDefaults.
If you are going to save too much suggestions with long texts and several fields you will need to implement a database. Core Data would be a good solution for your problem.

An easy (single line of code) way to do this is to use plists and the command
- (BOOL)writeToFile:(NSString *)path atomically:(BOOL)flag
on an NSArray
From the description:
Writes the contents of the array to a file at a given path.
If the array’s contents are all property list objects (NSString, NSData, NSArray, or NSDictionary objects), the file written by this method can be used to initialize a new array with the class method arrayWithContentsOfFile: or the instance method initWithContentsOfFile:. This method recursively validates that all the contained objects are property list objects before writing out the file, and returns NO if all the objects are not property list objects, since the resultant file would not be a valid property list.
In your example you would have (say) an NSArray of NSDictionaries with each NSDictionary containing the suggestions. Calling writeToFile would write it out in the event of no network availability. Then (say on app foreground event) you would read the file (arrayWithContentsOfFile call). If there is any content you would try to push to the server again.

You can use NSUserDefaults.
//write to NSUserDefaults
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:[NSNumber numberWithInteger:userRatingInteger] forKey:#"userRating"];
[defaults synchronize];
//read from NSUserDefaults
NSNumber *userRatingNumber = [defaults objectForKey:#"userRating"];
if (userRatingNumber) { // the user has rated.
// Use the value as needed.
}

Related

-[__NSCFDictionary setObject:forKey:]: mutating method sent to immutable object Return then Get from NSUserDefaults

The following code is returning an exception with the following error message and My Application Crashed, in my code all data store in NSMutableDictionary and then store in NSUserDefaults
After get the data and assign global NSMutableDictionary and i will try to update data in NSMutableDictionary app crash and showing error
-[__NSCFDictionary setObject:forKey:]: mutating method sent to immutable object
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSMutableDictionary * cacheUploadDataDic = [[defaults objectForKey:#"SalaryIncomeData"] mutableCopy];
Exactly, it crashes with mutable type issue, all the objects you retrieved from user defaults are inmutable, it uses the plist file to serialized the data list to file, check the plist programming guide about the supported types.
You shall regard the user defaults as a simple plist file storage wrapper.
Use NSMutableDictionary's 'initWithDictionary:' method instead.
Since an answer to the question in my comment does not appear to be forthcoming, I'm just going to go out on a limb and make the guess that your cacheUploadDataDic dictionary contains some more NSDictionary objects inside of it, and that your crash is occurring when you try to mutate one of those dictionaries. This fails because mutableCopy performs a shallow copy; only the dictionary object itself is copied, and all of the objects inside the dictionary, including any additional dictionaries, remain their original immutable selves.
You can fix this by making a deep copy of the dictionary instead. There are a few ways to do this, but the simplest is probably to use the CFPropertyListCreateDeepCopy function. Unfortunately, we have to do some bridging, since this API is only available at the CoreFoundation level, the reason for which is one of those eternal mysteries.
Anyway, do something like this:
NSDictionary *dict = [[NSUserDefaults standardUserDefaults] objectForKey:#"SomeKey"];
NSMutableDictionary *mutableDict = CFBridgingRelease(
CFPropertyListCreateDeepCopy(kCFAllocatorDefault,
(__bridge CFDictionaryRef)dict,
kCFPropertyListMutableContainersAndLeaves)
);
You will now be able to mutate the contents of mutableDict to your heart's content.
Follow your code, there shows no problem.
Then if possible, you can check the class of cacheUploadDataDic before update data and after NSMutableDictionary * cacheUploadDataDic = [[defaults objectForKey:#"SalaryIncomeData"] mutableCopy].
If there are one NSMutableDictionary and the other is NSDictionary then you must change cacheUploadDataDic at other line.
if you can't find out where you change the object,you can try KVO.

NSUserDefaults not saving

I am having a problem in my sprite kit app where my NSUserDefaults variable is not working. In createSceneContents (which I know is being called)
if (![defaults objectForKey:#"obj"]) {
difficultyLabel.text = #"Difficulty: Easy";
[defaults setObject:#"Easy" forKey:#"obj"];
} else {
difficultyLabel.text = [NSString stringWithFormat:#"Difficulty: %#", [defaults objectForKey:#"diff"]];
}
and then when you click on the SKLabelNode to change the difficulty and this code is being called
if ([label.text isEqualToString:#"Difficulty: Easy"]) {
label.text = #"Difficulty: Hard";
[defaults setObject:#"Hard" forKey:#"obj"];
NSLog(#"%#",[defaults objectForKey:#"obj"]);
} else {
label.text = #"Difficulty: Easy";
[defaults setObject:#"Easy" forKey:#"obj"];
NSLog(#"%#",[defaults objectForKey:#"obj"]);
but when I stop the program and run it again, it always just says Difficulty: Easy. Any suggestions?
As rmaddy's answer explains, NSUserDefaults won't immediately write data to long-term storage.
The values are saved in temporary memory when you call setObject:forKey:, so if you ever try to call objectForKey: after setObject:forKey:, it will return the value in memory if it exists, and if not, it goes to look for what's been saved to long-term storage.
In the background, the device will eventually save these values to permanent storage. It's something the OS handles, but in normal operation of your app, it shouldn't be necessary to immediately store these values to permanent storage most of the time, so let the OS do this at times when its been optimized to do so (I imagine the OS probably synchs every app's user defaults at once on some regular schedule, and as much as possible, tries to do this when the processor is idle probably).
But with that said, as rmaddy explains, if you've got something that crucially needs to be saved to the permanent storage immediately, you can always call synchronize. Keep in mind though... as long as your app isn't killed while in debug mode, the values you've set to be stored in user defaults will be stored.
But for your own sake, if you want to be absolutely certain, you can put it a call to synchronize in applicationDidEnterBackground as rmaddy suggests. Keep in mind though, this method isn't called if you kill the app.
When you say you "stop the program", what do you mean? Are you running in the debugger and killing the app and rerunning it again? NSUserDefaults doesn't persist your changes immediately. Either call [defaults synchronize] after writing important data or at least add such a call to your app delegate's applicationDidEnterBackground method. And then be sure you put your app in the background before killing it to ensure the data is written first.
To store:
NSString * str = #"name123";
NSUserDefaults *def=[NSUserDefaults standardUserDefaults];
[def setObject:str forKey:#"Key"];
[def synchronize];
NSLog(#"str = = %#",str);
To Retrieve:
NSUserDefaults *def=[NSUserDefaults standardUserDefaults];
NSString *str2 = [def valueForKey:#"Key"];
NSLog(#" Saved str = = %#",str2);
I found another reason why NSUserDefaults not saving,following code can make value in NSUserDefaults not saving:
NSUserDefaults *def= [[NSUserDefaults standardUserDefaults] init];
...
NSUserDefaults *def= [[NSUserDefaults standardUserDefaults] initWithSuiteName:#"123"];
You can search NSUserDefaults in your project to catch those init twice bug.
I had this issue where it was working previously but then wasn't. I couldn't figure it out.
It turned out restarting my mac fixed it. There must be some issue with the prefsd daemon that runs in the background that can cause this I'm guessing

UIWebView must get localStorage item and store it in the app natively / retrive it natively

I have the first part figured out...
- (void)webViewDidFinishLoad:(UIWebView*) myTapView {
NSString *jsString = #"localStorage.getItem('username');";
NSString *someKeyValue = [tapView stringByEvaluatingJavaScriptFromString:jsString];
// This is where I would start storing it so I can retrieve it later
}
So here is my issue, I need to store the value I just got from localStorage so I can use it again. Problem is I don't know where to start. This code is inside my ViewController for the WebView, so I need to be able to use it in other areas in the ViewController.m file.
What is the best method for storing the info? I would also want the above code to be able to update the value, as it won't always be the same username (people can switch accounts).
There are a few ways to store data on iOS, but it depends on what kind of value you want to store. If it's just a plain string or other "light" data, the best approach is to save it to NSUserDefaults. If it's an image or video, you may want to consider archiving it using NSKeyedArchiver. Otherwise, if you want to build a complicated data model, use Core Data.
Here's a good post on NSHipster that describe in detail pros and cons of each approach.
According to your code, you want to store a string. I'd go with NSUserDefaults in this case.
Use NSUserDefaults to store UIWebView local storage value as follows
- (void)webViewDidFinishLoad:(UIWebView*) myTapView {
NSString *jsString = #"localStorage.getItem('username');";
NSString *someKeyValue = [myTapView stringByEvaluatingJavaScriptFromString:jsString];
// Store your Username in iPhone persistence variable and use it later in the application
NSUserDefaults *userdefault = [NSUserDefaults standardUserDefaults];
[userdefault setObject:someKeyValue forKey:#"username"];
[userdefault synchronize];
//use of User default
NSLog(#"User name %#",[userdefault valueForKey:#"username"]);
}

How to make some config file to my program?

How to make some config file to my program so program can load settings from it befoce starting?
Thanks a lot!
It sounds like what you are interested in is some kind of user preferences, yes?
use NSUserDefaults class for application configuration. To establish the initial set of values before user customization make a plist file with the appropriate keys and values corresponding to what you wish to use in your application.
example of registering your default settings file with the system:
// Load default defaults.
// Defaults need to be registered prior to interface loading.
NSString* defaultsPath = [[NSBundle mainBundle] pathForResource:#"defaults" ofType:#"plist"];
NSDictionary* defaultDict = [NSDictionary dictionaryWithContentsOfFile:defaultsPath];
NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
[defaults registerDefaults:defaultDict];
then in your application logic you can simply ask the NSUserDefaults object for a value (any of the plist supported types) for a given key using the various methods of NSUserDefaults:
NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
BOOL myBool = [defaults boolForKey:#"myBool"];
// you can also get strings, data, numbers, dates, arrays, and dictionaries

NSUserDefaults not being saved in simulator ,even though I use synchronize

I add a dictionary to NSUserDefaults and call 'synchronize' method,but when I close my app and reopen it the values which are saved in NSUserDefault are nil.
#define kStartTime #"startTime"
#define kEndTime #"endTime"
NSUserDefaults *defaults=[NSUserDefaults standardUserDefaults];
NSString *startTime=#"08:00";
NSString *endTime=#"18:00";
NSDictionary *dict=[[NSDictionary alloc] initWithObjectsAndKeys:startTime,kStartTime,
endTime,kEndTime,
nil];
[defaults registerDefaults:dict];
[defaults synchronize];
And in the viewDidLoad method I call this:
NSString *startTimeValue=[defaults stringForKey:kStartTime];
the startTimeValue is still nil When I reopen my app in simulator.Is UserDefaults can't save in Simulator???
My point is when i close my app and reopen it in simulator the values are gone.
After I searched the apple document ,I got this.
registerDefaults:.
As is said in Apple Document :Adds the contents of the specified dictionary to the registration domain.
*The contents of the registration domain are not written to disk;*you need to call this method each time your application starts.You can place a plist file in the application's Resources directory and call registerDefaults: with the contents that you read in from that file.
So the function of the registerDefaults: is setting Default values in app,The Default value will not stored in disk by synchronize
I think you try to read the value from NSUserDefaults before you have registered the defaults.
If you register defaults these values are not saved to disk, they will not persist to the next application launch. You have to register the default values every time you launch the app.
When you read an object from NSUserDefaults and you have not saved a value before, the registered default value will be returned. If you have saved a value before, this value will be returned.
In the code you posted you have not saved anything to userDefaults. So the value you have registered as default will be returned. If you haven't registered any values this will return nil.
The code that registers the defaults should be put in a method that runs very early in the application life cycle. I would recommend to use application:didFinishLaunchingWithOptions:
If you want to save multiple values to NSUserDefaults you have to use multiple setObject:forKey: calls.
registerDefaults: can not be used to save multiple keys to NSUserDefaults at the same time!
NSUserDefaults *defaults=[NSUserDefaults standardUserDefaults];
NSDictionary *dict=[[NSDictionary alloc] initWithObjectsAndKeys:startTime,#"kStartTime",
endTime,#"kEndTime",
nil];
[defaults registerDefaults:dict];
[defaults synchronize];
NSString *startTimeValue=[defaults stringForKey:#"kStartTime"];
NSLog(#"%#",startTimeValue);
you need to add keys like #"value" like this.
Now try this code....

Resources