Directly add to array in defaults - ios

Is it okay to call this?
[[[NSUserDefaults standardUserDefaults] objectForKey:#"searchEnginesOrder"] addObject:#"eBay"];
I have a UITableView whose cells I want to load directly from the defaults, and I also want to modify those defaults. I'm wondering if the above line actually has any effect (when I NSLog the default's array searchEngineOrder it's null, I'm wondering if it's because the above code isn't actually adding to the defaults.

Generally speaking, that line of code would crash. User defaults returns immutable objects so calling addObject: will throw an exception.
You also shouldn't rely on changes made to returned objects being backed into the data store - this isn't user defaults specific, it goes for any API which doesn't document it as supported.
You should be separating your logic between your working data and your stored data with defined modifications and save points. Ensure that you mutable copy the data you extract from user defaults. You should also use registerDefaults: to setup initial values so you don't need to check for existence.

NSUserDefaults returns immutable objects. You have to create mutable copies in order to edit the values.
NSMutableDictionary *subSettings;
subSettings = [[[NSUserDefaults standardUserDefaults]
objectForKey:YOUR_SUBMAP_KEY] mutableCopy];
[subSettings setObject:YOUR_NEW_VALUE forKey:YOUR_VALUE_KEY];
[[NSUserDefaults standardUserDefaults]
setObject:subSettings forKey:YOUR_SUBMAP_KEY];
[subSettings release];

Related

Working with Integer values for NSUserDefaults

I am trying to create some settings for my application. One of which is a sort setting which can be 0,1,2. At present I have an enum to handle this:
typedef NS_OPTIONS(NSInteger, SearchSort) {
SearchSortDefaultBestMatched = 0,
SearchSortDistance = 1,
SearchSortHighestRated = 2
};
When a value is selected from a picker I update the [NSUserDefaults standardDefaults] setInteger:
However, when a user first runs the app there is no default set. What I am finding is that if check the [[NSUserDefaults standardUserDefaults] integerForKey:...]; this returns 0. Which would be incorrect. Is this the correct process to store this value/check if the defaults has a value? Ideally I would want nil instead of 0 as 0 would potentially be an option.
Unless the proposed solution is to of course store the values as 1,2,3 and translate back and forth.
NSUserDefaults has a method registerDefaults for this very purpose. you give it a dictionary of "default default" key/value pairs. Those become the initial values that you get when you ask for a given key that has not been set.
I always add my initial default values in the +initialize class method for my app delegate. That method gets called before the system alloc/inits the app delegate, so it gets called before any of your app's custom code (Aside from the code in main) gets called.
EDIT:
As #AdamEberbach suggests in his answer, you can also use objectForKey: instead of integerForKey. That will return nil if no value is stored, or an NSNumber (who's value may be zero) if a value has been stored.
Why not use objectForKey: instead, if you get a valid object then you can cast to NSNumber and get the integerValue: - else you get a nil.

Pre populate core data with only one Managed Object

I need to pre populate my core data base with only one Managed Object.
Currently i'm checking on AppDelegate if it's the fisrt time that the app runs, and then a add the object, like this:
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
BOOL firstTime = [defaults boolForKey:#"firstTime"];
if (firstTime) {
[dataManager insertManagedObject:myManagedObect];
[defaults setBool:NO forKey:#"firstTime"];
[defaults synchronize];
}
insertManagedObject method checks if the managedObject is already in the data base.
It's working fine, but i'm afraid in future app updates this could cause me some kind of trouble, mainly if i change my data model and add a new data model version.
What's the best approach to do that?
Why don't you execute a fetch request a see if the store already
contains that managed object? e.g setting a specific identifier for
that managed object...
Following my comment you could just set up a fetch request against your entity and see if the store already has an instance for it.
This is simple enough to achieve.
If you need to query against a specific object, you could set a property identifier (i.e. a guid) for your entity and use a predicate to see if the object with the specific guid exists or not.
If you share some other details I can give you other suggestions...

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.

Best way to store user information for my iOS app

What kind of database do you suggest? I want to store user email, username, password, and a couple other random pieces of information. It doesn't have to be fancy. Just a simple database. Are there any free options?
The user information needs to be stored in the keychain to keep it secure.
Any other information could be stored in any one of:
User defaults NSUserDefaults
File on disk (maybe a plist)
Database Core Data (technically just a file on disk)
Which you choose depends on what the data is, how much there is and what kind of access you need to it.
If your data is small and chosen by the user as some kind of setting then user defaults makes sense and is the lowest cost for you to implement.
To use a database, check out Core Data intro.
Wain is right but I think as you want to store small amount of data for further use, the most efficient ways is to use NSUserDefault.
NSUserDefault stores data in NSDictionary type things.
I think this is the step you have to take:
1- check if data exists. I mean if user selected the number if the last run of your app. So in viewDidLoad method:
NSMutableDictionary *userDefaultDataDictionary = [[[NSUserDefaults standardUserDefaults] dictionaryForKey:ALL_DATA_KEY] mutableCopy];
if (userDefaultDataDictionary) {
// so the dictionary exists, which means user has entered the number in previous app run
// and you can read it from the NSDictionaty:
if(userDefaultDataDictionary[LABLE_KEY]){
//and store it
}
}
2 - you can implement some method like syncronize to store data in NSUserDefault every time something has been changed.
- (void) synchronize
{
NSMutableDictionary *dictionaryForUserDefault = [[[NSUserDefaults standardUserDefaults] dictionaryForKey:ALL_DATA_KEY] mutableCopy];
if(!dictionaryForUserDefault)
dictionaryForUserDefault = [[NSMutableDictionary alloc] init];
dictionaryForUserDefault[LABLE_KEY] = //data you want to store
[[NSUserDefaults standardUserDefaults] setObject:dictionaryForUserDefault forKey:ALL_DATA_KEY];
[[NSUserDefaults standardUserDefaults] synchronize];
}
P.S. and don't forget to #define your keys for your dictionary:
#define LABLE_KEY #"Lables"
#define ALL_DATA_KEY #"AllData"
Store it in a plist. If you're talking about data pertaining to one or a few users, that's probably the easy thing. here is a simple example.
Since you say database, store in Sqlite. There's some provided stuff for it already in xcode.
The entire database is contained in one file, which can be moved around if you need to.
Here is some more information on how to use one in your app.

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

Resources