iPhone code to create new NSUserDefaults objects? - iphone

I have NSUserDefaults storing a number of string variables for things like name, date of birth, address, etc. What I would like to know is how to write a code that will create a new object for each new user. For example, I have a spinning wheel that shows up immediately after the first time the user runs the app. What I want, is for that wheel to have one single option - "New User". Once that New User fills out a bunch of text fields that I am using NSUserDefaults to save, I want that user to be saved on that spinning wheel so that the next time they open up the app they have the option of returning to all of the variables that they previously put in, or creating a new user so they can input all new variables.
I know how to do everything except write the code to create new users automatically. Potentially, the program should allow for a limitless number of these user objects and then just use something arbitrary like their last name to input into the spinning wheel. I would assume that the code would need to be put somewhere in the following code used to save the NSUserDefaults:
NSUserDefaults *userData = [NSUserDefaults standardUserDefaults];
[userData setObject:txtName.text forKey:#"name"];
---EDIT FOR CLARIFICATION ----
I am able to put multiple strings into this 'userData' object already by simply adding more lines like the 2nd line from above. What I want to know is how to add 'user2Data', 'user3Data', 'user4Data', 'usernData'..........to make a potentially limitless amount of user objects to store these variables?

You can add NSArray objects to NSUserDefaults.
NSUserDefaults *userData = [NSUserDefaults standardUserDefaults];
NSMutableArray *users = [NSMutableArray mutableArrayWithArray:[userData objectForKey:#"users"];
[users addObject:txtName.text];
[userData setObject:[NSArray arrayWithArray:users] forKey:#"name"];
[userData setObject:txtName.text forKey:#"name"];
If you need more than a single string for user data, you can look at using an NSDictionary object.
All that said, if you're going to be storing a lot of data, you should look at maybe saving .plist files in your app's Library directory.

You could modify the following to meet your needs, to be put into your app delegate:
+ (void) initialize
{
if ([self class] == [MyAppDelegate class]) {
// initialize user defaults dictionary
BOOL isFirstTimeRun = YES;
...
NSMutableDictionary *resourceDict = [NSMutableDictionary dictionary];
[resourceDict setObject:[NSNumber numberWithBool:isFirstTimeRun] forKey:kIsFirstTimeRunKey];
...
[[NSUserDefaults standardUserDefaults] registerDefaults:resourceDict];
}
}
...
- (void) applicationDidFinishLaunching:(UIApplication *)application
{
if ([[[NSUserDefaults standardUserDefaults] objectForKey:kIsFirstTimeRunKey] boolValue]) {
// you could do first-time-run stuff here, such as initializing
// other data model elements...
}
...
}
...
- (void) applicationWillTerminate:(UIApplication *)application
{
// if we're quitting the app, it must have been run at least once
if ([[[NSUserDefaults standardUserDefaults] objectForKey:kIsFirstTimeRunKey] boolValue])
[[NSUserDefaults standardUserDefaults] setObject:[NSNumber numberWithBool:NO] forKey:kIsFirstTimeRunKey];
...
}

What you probably want to do is create a property list for each user, and save the list of users in your NSUserDefaults. Once a user has been selected, you can then load the contents of the property list for that specific user and load/store all data in that particular property list. The basics of this approach is to use an NSDictionary* wherever you need to read from the property list and an NSMutableDictionary* whenever you need to both read and write to it. When you first construct the property list, you simply instantiate an NSMutableDictionary. When you want to save it or load it, you can use the NSPropertyListSerialization class in order to save or load the dictionary from/to a property list.

Related

adding an item to at array in NSUserdefault

I am new to iOS development and could not find a way to solve this problem:
I have an app that has two views: one where the user enters some information (say a string), and another view where there is a tableview that includes all the strings that were ever entered (like a history view).
What I am trying to find is a good way to store the input string, then load it into the table view data source once the user switches to the history. I tried to use NSUserdefault but with not much success. Just getting messed up with the data structures, etc.
Here is what I am doing on the main view (where the user enters the input string):
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSMutableArray *arr1 = [[NSMutableArray alloc] init];
arr1 = [defaults arrayForKey:#"historyNames"];
[arr1 addObject:string];
[defaults setObject:arr1 forKey:#"historyNames"];
From some reason I get a warning where I read to arr1, and honestly, I doubt that should work anyway.
Can anyone suggest how I could modify this to work properly and achieve what I am looking for?
Thanks.
[defaults arrayForKey:#"historyNames"];
Will return nil if you never initialized and saved an array for that key in NSUSerDefaults.
If you initialize and array and set it once (look up how to initialize default values for NSUserDefaults), it will return a proper array.
Then you can just do
NSMutableArray *arr1 = [NSMutableArray arrayWithArray:[[defaults arrayForKey#"historyNames"]];
Depending on how many elements this array will have, you may be better off using Core Data. Using user defaults is not very efficient for many/large values, just for small settings and things like that.
When your application starts up, look in user defaults to see if you have an array object already from the last time you used it. If there isn't one, call alloc and init for arr1. (You don't want to call it if you're accessing it from defaults.)
NSMutableArray * arr1;
arr1 = (NSMutableArray *) [defaults objectForKey:#"historyNames"];
if (!arr1) {
arr1 = [[NSMutableArray alloc] initWithCapacity:20];
}
In your main view, just add the input string, and save the defaults.
NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
[prefs setObject: arr1 forKey: #"historyNames"];
[prefs synchronize];

JSON and Core Data:Should I convert JSON into Core Data?

I'm new to Core Data and I need some help on my project.
I'm developing iPhone app that get JSON (Restaurants info) from server and shows the location on the map and table view, and stores favorite restaurants by pressing "add favorite" button.
For now, I'm just using NSDictionary and its functions to display data on the table and annotations and having favoriteRestaurant entity to store favorite restaurant data.
However, I would like to convert the NSDictionary object into Core Data object (Restaurant), and add "BOOL isFavorite" attribute to it and then delete favoriteRestaurant entity.
Make function that saves the restaurant object that passed and changes its "isFavorite" state, which is triggered by "add Favorite" button.
The favorite table shows only the restaurants that has been saved and isFavorite = YES.
I would like to know if this is right approach to accomplish what I want.
Thank you in advance!
Hi, thank you for fast responses. I forgot to say that I also want to implement MKAnnotation to that class so each annotation pin on the map belongs to unique restaurant object. If I want to do this, should I have another favorite class or Core Data entity, or just save it in the Restaurant table and make isFavorite = YES? Thank you, again!
If you're only concerned about saving the favorites then id go with plist serialization. I do the same thing in multiple apps. Normally I create a Helper class like below.
#import "Favorites.h"
#import "NSArray+Locations.h"
#implementation Favorites
+(void)addFavorite:(Location *)location{
NSMutableArray *remove = [NSMutableArray array];
[location.data enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
NSLog(#"%#,%#, %#",key,obj,[obj class]);
if([obj isKindOfClass:[NSNull class]])
[remove addObject:key];
}];
[location.data removeObjectsForKeys:remove];
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSMutableDictionary * favorites = [NSMutableDictionary dictionaryWithDictionary:[defaults objectForKey:FAVORITES]];
if(!favorites)
favorites = [NSMutableDictionary dictionaryWithCapacity:1];
[favorites setObject:location.data forKey:[location getID]];
[defaults setObject:favorites forKey:FAVORITES];
[defaults synchronize];
}
+(void)removeFavorite:(Location *)location{
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSMutableDictionary * favorites = [NSMutableDictionary dictionaryWithDictionary:[defaults objectForKey:FAVORITES]];
[favorites removeObjectForKey:[location getID]];
[defaults setObject:favorites forKey:FAVORITES];
[defaults synchronize];
}
+(NSArray *)getFavorites{
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSMutableDictionary * favorites = [defaults objectForKey:FAVORITES];
return [NSArray arrayWithLocations:[favorites allValues]];
}
+(BOOL)isFavorited:(Location *)location{
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSMutableDictionary * favorites = [defaults objectForKey:FAVORITES];
for(NSString *key in [favorites allKeys])
if([[location getID] isEqualToString:key])
return YES;
return NO;
}
#end
In this case the Location object is just a wrapper around a dictionary to make accessing the fields simpler.
If you want to save all of your data and not just favorites then I would go with core data otherwise a very large plist could give you memory headaches.
I like your approach. In my opinion Core Data is the way to go.
If you get lots of JSON records (say, dozens or hundreds of restaurants) with maybe even more data fields in the future, you could run into memory problems when using an array of NSDictionarys. Remember, a serialized plist can only be retrieved entirely, so if it gets large you will have to keep all the data in memory.
Also, you will very like have a much better performance from the start.
Your BOOL attribute (in Core Data that would be a NSNumber) should work fine for your purpose.

what is the best way for saving username and High Score

In my app I need to save a double value (high score) and String (player name) what should i use to get this.
any idea will be great.
Thank you
If this is all you're saving then NSUserDefaults should be fine
// To store
[[NSUserDefaults standardUserDefaults] setObject:name forKey:#"name"];
[[NSUserDefaults standardUserDefaults] setDouble:score forKey:#"score"];
// To read back in
NSString *name = [[NSUserDefaults standardUserDefualts] objectForKey:#"name"];
double score = [[NSUserDefaults standardUserDefaults] doubleForKey:#"score"];
// Don't forget that your name is autoreleased - if you want to keep it, set it to a retained
// property or retain it yourself :)
As deanWombourne said, you can use NSUserDefaults to store these data but it isn't very secure. If you don't want to store this data "in the air", you can take a look to SFHFKeychainUtils by Buzz Andersen to store them in the iPhone Keychain.
First of all, copy SFHFKeychainUtils files to your project. Click on the SFHFKeychainUtils.m and click on Get Info. Go to Target tab and check if the box near your target is checked. If not, check it. Control-click on your Framework folder and select Add Existing Framework. Find Security.framework and add it to your project. Check also that this framework is added to your target by doing the same procedure done for SFHFKeychainUtils.m. Now open you implementation file on where you want to use this code and add on the top #import "SFHFKeychainUtils.h".
This is a little example on how to use this code:
// to store your data
NSError *error = nil;
[SFHFKeychainUtils storeUsername:kName andPassword:name forServiceName:kStoredName updateExisting:YES error:&error];
[SFHFKeychainUtils storeUsername:kScore andPassword:score forServiceName:kStoredScore updateExisting:YES error:&error];
// to get them back
NSString *name = [SFHFKeychainUtils getPasswordForUsername:kName andServiceName:kScoredName error:&error];
double score = [SFHFKeychainUtils getPasswordForUsername:kScore andServiceName:kScoredScore error:&error];
// kName, kScore, kStoredName, kStoredScore are defined key but you can use also strings with #"your string here".
// It is important that when you store and get back a value, username and serviceName must be the same.

NSUserDefaults not present on first run on simulator

I've got some settings saved in my Settings.bundle, and I try to use them in application:didFinishLaunchingWithOptions, but on a first run on the simulator accessing objects by key always returns nil (or 0 in the case of ints). Once I go to the settings screen and then exit, they work fine for every run thereafter.
What's going on? Isn't the point of using default values in the Settings.bundle to be able to use them without requiring the user to enter them first?
If I got your question right, in your app delegate's - (void)applicationDidFinishLaunching:(UIApplication *)application, set the default values for your settings by calling
registerDefaults:dictionaryWithYourDefaultValues
on [NSUserDefaults standardUserDefaults]
- (void)applicationDidFinishLaunching:(UIApplication *)application {
NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithInt:3], #"SomeSettingKey",
#"Some string value", #"SomeOtherSettingKey",
nil];
[ud registerDefaults:dict];
}
These values will only by used if those settings haven't been set or changed by previous executions of your application.
As coneybeare said "You should detect if it is the first load, then store all your defaults initially."
On applicationDidFinishLaunching try to set default value in your preference.
Here is the sample:
NSUserDefaults *defaults =[NSUserDefaults standardUserDefaults];
if([defaults objectForKey:#"YOUR_KEY"] == nil)
{
[defaults setValue:#"KEY_VALUE" forKey:#"YOUR_KEY"];
}
When application will run second time it will come with KEY_VALUE for YOUR_KEY.
Thanks,
Jim.
Isn't the point of using default
values in the Settings.bundle to be
able to use them without requiring the
user to enter them first?
No. The point of the settings bundle is to give the user a place to edit all 3rd Party app settings in a convenient place. Whether or not this centralization is really a good idea is a User Experience issue that is off topic.
To answer your question, you should detect if it is the first load, then store all your defaults initially.
And while we are on the subject, I would also check out In App Settings Kit as it provides your app with a simple way to display your app settings in both places (in-app and Settings.app) with minimal code.
The values in the Settings.bundle are intended for the Settings app to able to fill in default values for your app. They are not used by your own app.
But you can set defaults yourself with the registerDefaults: method of NSUserDefaults. This will not actually set them on disk but just give "defaults for the defaults": they are used when no value has been set by the user yet.
Setting registerDefaults: must be done before any use of the default values. The "applicationDidFinishLaunching:" method that others suggested for this, is too late in most cases. By the time "applicationDidFinishLaunching:" is called, your views have already been loaded from the nib files, and their "viewDidLoad:" methods have been called. And they may typically read user defaults.
To guarantee that the defaults are set before first use, I use the following utility class, which loads the values from the Root.plist file and sets them with "registerDefaults:". You use this class to read user defaults instead of "[NSUserDefaults standardUserDefaults]". Use "[Settings get]" instead.
As a bonus, it also contains a registration method for user default change notifications, because I always forget how that is done.
#import "Settings.h"
#implementation Settings
static bool initialized = NO;
+ (void) setDefaults
{
NSString *bundlePath = [[NSBundle mainBundle] bundlePath];
NSString *settingsBundlePath = [bundlePath stringByAppendingPathComponent:#"Settings.bundle"];
NSBundle *settingsBundle = [NSBundle bundleWithPath:settingsBundlePath];
NSString *settingsPath = [settingsBundle pathForResource:#"Root" ofType:#"plist"];
NSDictionary *settingsDict = [NSDictionary dictionaryWithContentsOfFile:settingsPath];
NSArray *prefSpecifierArray = [settingsDict objectForKey:#"PreferenceSpecifiers"];
NSMutableDictionary *appDefaults = [[NSMutableDictionary alloc] init];
for (NSDictionary *prefItem in prefSpecifierArray)
{
NSString *key = [prefItem objectForKey:#"Key"];
if (key != nil) {
id defaultValue = [prefItem objectForKey:#"DefaultValue"];
[appDefaults setObject:defaultValue forKey:key];
}
}
// set them in the standard user defaults
[[NSUserDefaults standardUserDefaults] registerDefaults:appDefaults];
if (![[NSUserDefaults standardUserDefaults] synchronize]) {
NSLog(#"Settings setDefaults: Unsuccessful in writing the default settings");
}
}
+ (NSUserDefaults *)get
{
if (!initialized) {
[Settings setDefaults];
initialized = YES;
}
return [NSUserDefaults standardUserDefaults];
}
+ (void) registerForChange:(id)observer selector:(SEL)method
{
[[NSNotificationCenter defaultCenter] addObserver:observer selector:method name:NSUserDefaultsDidChangeNotification object:nil];
}
+ (void) unregisterForChange:(id)observer
{
[[NSNotificationCenter defaultCenter] removeObserver:observer name:NSUserDefaultsDidChangeNotification object:nil];
}

how can i store value in an NSArray using WritetoFile?

i wana store the index of seleted cell of table using NSArray, can u help me....
You can use user defaults or property list for this.
Example on user defaults. You have a controller class that has access to the index and will load it at startup and write it into plist whenever it's updated:
If you have some kind of controller class then you would put this code into + (void)initialize, it initialises the variable if it does not exists in plist:
+ (void)initialize
{
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSDictionary *appDefaults =
[NSDictionary dictionaryWithObject:[NSNumber numberWithInteger:5]
forKey:#"MyFunnyIndex"];
[defaults registerDefaults:appDefaults];
}
In your -(void)awakeFromNib (I'm assuming you're using some kind of controller class) load your last stored value:
-(void)awakeFromNib
{
int index =
[[NSUserDefaults standardUserDefaults] integerForKey:#"MyFunnyIndex"];
[somethingThatNeedsIndex setIndex:index];
// ...
}
Somewhere where the index is updated (or where you want to write it to plist), let's call it - (void)updateInterface:
- (void)updateInterface
{
[[NSUserDefaults standardUserDefaults]
setObject:[NSNumber numberWithInteger:index]
forKey:#"MyFunnyIndex"];
[[NSUserDefaults standardUserDefaults] synchronize];
}
I don't know if I understand the question correctly, but it sounds like you could use a property list to store this information. Property lists are very easy to use and quite efficient with small amounts of data.
Read the "Property List Programming Guide" for further explanation. There is even a tutorial in there.