Why does this NSUserDefaults key contain a dot? - ios

I'm watching the lesson of standford CS193P, in particular the lecture n°7. I have some doubts about NSUserDefaults. This is the part of code :
#define FAVORITES_KEY #"CalculatorGraphViewController.Favorites"
- (IBAction)addToFavorites:(id)sender
{
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSMutableArray *favorites = [[defaults objectForKey:FAVORITES_KEY] mutableCopy];
if (!favorites) favorites = [NSMutableArray array];
[favorites addObject:self.calculatorProgram];
[defaults setObject:favorites forKey:FAVORITES_KEY];
[defaults synchronize];
}
I read the documentation about NSUserDefaults, but I don't understand this code in particular [[defaults objectForKey:FAVORITES_KEY] mutableCopy]. FAVORITES_KEY is #"CalculatorGraphViewController.Favorites".
My question is why I should should use CalculatorGraphViewController.Favorites? I don't understand the dot! It seems to me that a structure of getter or setter but Favorites have a capital letter and then CalculatorGraphViewController.Favorites doesn't make sense.
Can You help me please?

You can think of it as a NSDictionary, and the key you provide is only for your own reference. It is for you to retrieve the value back later. You can call it a string like #"CalculatorGraphViewController.Favorites" or any other string that you like. They name it this way just to identify that this is the value for Favorites choices recorded in CalculatorGraphViewController, I believe.

As others have noted, the key is an arbitrary string. The catch is that there can be many parts of your app writing into the defaults. If you picked something really simple as the key, say “favourites”, it could very well happen that two parts of your app would both attempt to use the same key for something different. (Say, for favourite artists and favourite songs.)
This is a common problem in programming, and it’s usually solved by introducing a namespace, or some kind of prefix that makes the clashes less probable. You can often see namespaces in Java classes, something like com.someguy.AppName.SomeClassName. Or even in domain names – like developer.facebook.com and developer.apple.com. Both use the term “developer”, but both differ in the namespace (com.facebook versus com.apple).
You can use the same solution in your use case and introduce a namespace into your defaults key. A logical choice for the namespace is the class name, because you are unlikely to have two classes with the same name. Thus you arrive at the CalculatorGraphViewController prefix. The dot is just a customary way to separate the components in the namespace.

It is just key, it can be any string or object, but not nil

Related

Is it ok to read a value stored in NSUserDefaults in multiple views

Is it ok to read a value stored in NSUserDefaults from multiple views or you only read it once and then you use another method to pass the data to different part of your app?
In other words I want to know if I’m doing it right, what I’m currently doing in an app I’m working on is basically saving a couple of NSIntegers and NSStrings (only two or three of each) in NSUserDefaults and then I’m reading those values in different parts of my app (different views) but I was wondering if this is a common practice or should I be doing something different like, read the value somewhere in the app and then try to use a different method to pass that data to other views. I want to learn best programming practices, that’s all.
What is the most common practice when using NSUserDefaults values in multiple parts of your app?
FYI,
I’m familiar with multiple ways to pass data between view controllers such as, delegation, prepareForSegue etc.
Thanks a lot.
I would also recommend, to read it multiple times and do not introduce another layer to hold the data.
The most important aspect is imo the actuality of the data, which might be changed inbetween different invocations.
It is ok to read and even write values to NSUserDefault in multiple places, but it is a better practice to have a global mechanism (like a singleton pattern) to read and write to UserDefaults. this way you'll be guaranteed to have fully synchronized values. all you need to do is to create a new class and add a few Class methods to read and write values from NSUserDefaults.
It is OK, when you call [NSUserDefaults standardUserDefaults] it will return the same object whether you spread the calls everywhere in the app or you encapsulate the access in a class.
I prefer the later as it allows you to have more readable code (and other benefits):
BOOL hasX = [TLPSettings hasPreferenceX];
if (hasX) {
[TLPSettings setY:YES];
}
vs
BOOL hasX = [[NSUserDefaults standardUserDefaults] boolForKey:#"hasX"];
if (hasX) {
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:#"hasY"];
[[NSUserDefaults standardUserDefaults] synchronize];
}
You can change the preferences keys easily (without defining consts for them), change the validation or logic of a preference without messing with it everywhere, debug its usage or rogue values easily, store all or part of them in a different place, etc.
TLP is your three letter prefix.

Valueforkey in NSUserDefaults

I am new to IOS programming and am trying to modify some code a developer wrote for me. I'm having problems in the following code
NSUserDefaults *pref=[NSUserDefaults standardUserDefaults];
NSString *strUrl=[pref valueForKey:#"HistoryUrl"];
if (strUrl.length>0)
{
newUrl=strUrl;
}
else
{
newUrl=#"http://www.google.com";
}
The HistoryUrl parameter seems to have the value 'http://www.yahoo.com' stored in it. I've looked everywhere and searched the net on how to replace this value to google's address. I have even gone through all the code in XCode and can't find where historyurl is declared:
Where is HistoryUrl declared?
How can it be modified?
Thanks in advance!
#"HistoryUrl" is an NSString* containing the string HistoryURL. Thats' how you write an NSString* with fixed data.
pref is an object representing the user's preferences.
The user's preference contain multiple key - value pairs. For example there might be a key named "HistoryUrl" which might have some value.
The valueForKey: method reads the value that is stored under the key "HistoryUrl" and stores it into strUrl. If there is no key named "HistoryUrl" then the result will be nil. (The use of valueForKey: is strange, because it is not a method of NSUserDefaults itself; typically one would use objectForKey:)
The following code checks whether the value read has any characters in it (length is roughly speaking the number of characters); if there are any characters then newUrl is set to that value; if there were no characters then newUrl is set to the NSString* "http:/www.google.com".
So someone at some time has stored a value under the name "HistoryUrl" into the application's preference file. You remove that value by calling
[pref removeObjectForKey:#"HistoryUrl"]
Or, since you don't seem to want anything other than "google", remove all the code and just write NSString* newUrl = #"http://www.google.com" if that's what you want.
HistoryURL is an arbitrary key and it is being used in your code to retrieve a value from NSUserDefaults. At some stage in your code, you will want to use setObject:forKey: to update the value stored in NSUesrDefaults. You will also need to call synchronize to save the new value after it has been set.
Where is HistoryUrl declared?
It's not. #"HistoryUrl" is just a string. Read up on NSUserDefaults to learn how it all works, but in a nutshell the defaults system is like an associative array (also known as a dictionary or map), where you can key/value pairs. In this case, #"HistoryUrl" is the key, and #"http://www.yahoo.com" is the value. You can make up whatever keys you want for storing your values.
How can it be modified?
Do you want to modify the key, or the value associated with the key? If the former, just make up a different key and use it. If the latter, use the methods of NSUserDefaults to set a different value:
NSUserDefaults *pref=[NSUserDefaults standardUserDefaults];
[pref setObject:#"http://google.com" forKey:#"HistoryUrl"];
[pref synchronize];
Note: the -synchronize call isn't strictly necessary, as the system will generally write your change eventually. But a lot of people like to call it whenever they make a change to the defaults system.

Store an array of UIViews in NSUserDefaults

I'm trying to add an array of uiviews to NSDefault but it doesn't seem to be keep the array. Does any one know why? I also tried to store each view in nsvalue before storing it in nsdefault which still didn't work.
NSArray *arr = [[NSArray alloc] initWithObjects:[NSValue valueWithNonretainedObject:myView], nil]];
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:arr forKey:#"myKey"];
NSArray *resultArray = [defaults objectForKey:#"myKey"];
and resultArray is nil!
Thanks
the reason why I'm trying to do this is because these are the header views of my uitableview. Since it takes time to create them I wanted to create them only once and store them for future access.
From the docs for NSUserDefaults:
The NSUserDefaults class provides convenience methods for accessing common types such as floats, doubles, integers, Booleans, and URLs. A default object must be a property list, that is, an instance of (or for collections a combination of instances of): NSData, NSString, NSNumber, NSDate, NSArray, or NSDictionary. If you want to store any other type of object, you should typically archive it to create an instance of NSData. For more details, see Preferences and Settings Programming Guide.
If you want to put a UIView (why?) in NSUserDefaults, you need to archive it first into an NSData object.
But you need to ask yourself why you want to put a view in NSUserDefaults. You should only be putting bits of data in NSUserDefaults. Views display data. It's easy to redisplay a view once you have the data back. Consider just putting some needed data in NSUserDefaults.
Are you sure you want to do that? It is definitely better to store an array of models to the data base or some file and recreate views from them when needed.
A ha! You are not the first person to face this issue. I've not had this type of issue myself but, in the link below, is a blog with code that allows you to cache and re-use your views. Then you would only need to re-create the views when you launch. Example code:
Cache UIViews for re-use in tableview

Best way to save data to iOS?

In my application (iOS 5) I want to save data - I want to save debts.
So its:
plus or minus money
the amount of money
and the name who has the debts (or the name where you have the debts)
But I don't how to save the data (NSUserdefaults,Core data, SQLLite)
Maybe you can tell me the best way to save them?
The easiest way to store small amount of data on your device is to use NSUserDefaults. But only property lists could be saved in this way. A property list is a combination of objects of 6 types, NSNumber, NSString, NSArray, NSDictionary, NSDate, NSData.
In your case it's easy to do. For example, to save a new debt record you can use following method:
#define DEBTS_LIST_KEY #"listOfAllDebts"
#define DEBTOR_NAME_KEY #"debtorName"
#define DEBT_AMOUNT_KEY #"amountOfDebt"
-(void) saveDebt:(CGFloat) debtAmount forName:(NSString *) debtorName
{
// pointer to standart user defaults
NSUserDefaults * defaults = [NSUserDefaults standardUserDefaults];
// the mutalbe array of all debts
NSMutableArray * alldebtRecords = [[defaults objectForKey:DEBTS_LIST_KEY] mutableCopy];
// create new record
// to save CGFloat you need to wrap it into NSNumber
NSNumber * amount = [NSNumber numberWithFloat:debtAmount];
NSDictionary * newRecord = [NSDictionary dictionaryWithObjects:[NSArray arrayWithObjects:amount,debtorName, nil] forKeys:[NSArray arrayWithObjects:DEBT_AMOUNT_KEY, DEBTOR_NAME_KEY, nil]];
[alldebtRecords addObject:newRecord];
[defaults setObject:alldebtRecords forKey:DEBTS_LIST_KEY];
// do not forget to save changes
[defaults synchronize];
}
To readList of debts you have read something similar.
But I recommend you to use core data. It's more flexible and you won't have to write all this code to manage your data (to edit existed records, or to delete them). You will be able to extend your model much easier, for example, when you want to save the date of the debt. This is the link to a good tutorial
If the quantity of records is user-defined, and will grow with app use, I suggest Core Data, which can be backed by SQLite. If you are working in a modern Xcode (i.e. Xcode 4), creating models is easy and graphical. If you have ever worked with ORM frameworks before, the interface for querying, etc. should be easy to grasp.
Search around for some tutorials, but be specific about finding tutorials that match your version of Xcode, as Core Data development has been changing a lot lately.
Good and easy way is to create your own objects and serialize them using NSCodying and NSCopying
https://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Protocols/NSCopying_Protocol/Reference/Reference.html
https://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Protocols/NSCoding_Protocol/Reference/Reference.html

iOS store just a little bit of data

I was wondering if there is a way to store small amounts of data, without going to a full-blown core-data API. I just need to store 6 'double' values somewhere... What's the best approach for that?
Thanks, Alex.
Core Data is just one way to store data, and it only makes sense when you need the things that it does. Here are five good options for storing your data:
Use NSUserDefaults. (Dead simple.)
Store the data in an appropriate structure (say, NSDictionary) and store it as a property list. (Pretty darn easy.)
Store the data in a class of your own design that implements NSCoding, and then write an instance of that class to a file using NSKeyedArchiver. (Works well for storing entire object graphs; this is basically what IB does. It might take an hour or two for the light to come on, but once you understand it this is a very nice way to read and write objects.)
Use Cocoa Touch's file system API, notably NSFileHandle and NSFileManager. (Conceptually simple if you've ever worked with a file system before. Puts you in complete control.)
Use the regular old POSIX file system API. (Best for existing Unix code, or code that you also want to compile on other platforms.)
Before you jump into any of those, read Apple's Archives and Serializations Programming Guide, User Defaults Programming Topics, and File System Programming Guide.
You can use NSUserDefaults to accomplish that (http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/NSUserDefaults_Class/Reference/Reference.html).
-(void)saveToUserDefaults:(NSString*)myString
{
NSUserDefaults *standardUserDefaults = [NSUserDefaults standardUserDefaults];
if (standardUserDefaults) {
[standardUserDefaults setObject:myString forKey:#"Prefs"];
[standardUserDefaults synchronize];
}
}
-(NSString*)retrieveFromUserDefaults
{
NSUserDefaults *standardUserDefaults = [NSUserDefaults standardUserDefaults];
NSString *val = nil;
if (standardUserDefaults)
val = [standardUserDefaults objectForKey:#"Prefs"];
return val;
}
The easiest way to store small amounts of data without using some of the larger API's is the NSUserDefaults class. It's really easy to set up and use.

Resources