Unable to save array to NSUserDefaults - nsuserdefaults

This is my first attempt at using NSUserDefaults. I've read every question & answer posted in stackoverflow regarding this subject, but still can't get it to work. I must be missing something basic. The array (allContacts) merely contains a few names and phone numbers. Unless I'm misunderstanding what's happening, both fields are NSStrings. Or are they just pointers to strings? If that's the case, how would I convert them to actual NSStrings? Here's my code to save the array:
- (BOOL)saveChanges
{
NSMutableArray *mutableArray = [[NSMutableArray alloc] initWithArray:allContacts];
[[NSUserDefaults standardUserDefaults] setObject:mutableArray forKey:#"allContacts"];
[[NSUserDefaults standardUserDefaults] synchronize];
return 1;
}
Here my log:
2013-08-07 15:48:17.568 ImOK[5515:907] *** -[NSUserDefaults setObject:forKey:]: Attempt to insert non-property value '(
"Brad Pitt, 1-917-297-1234",
"Marilyn Monroe, 9179291234"
)' of class '__NSArrayM'. Note that dictionaries and arrays in property lists must also contain only property values.
Thanks in advance for any help.
This code "for(id contact in allContacts) { NSLog(#"%#", [contact class]); }" gives me:
2013-08-07 17:11:30.845 ImOK[5569:907] Contacts
2013-08-07 17:11:30.854 ImOK[5569:907] Contacts
so I guess they are not valid property values.
Incidentally, there was no intelligent reason for me to attempt to save the array as an NSMutableArray. I was just experimenting.

From the Apple documentation: https://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/NSUserDefaults_Class/Reference/Reference.html#//apple_ref/doc/c_ref/NSUserDefaults
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.
If allContacts is an NSArray, just use that in your setObject:forKey: call. If it's an NSMutableArray, use:
[allContacts copy]
to get a non-mutable copy of the array.

Related

NSLog pointer syntax

I'm a little bit confused about the syntax of NSLog. For example,
NSString *nameString = #"Name";
NSLog(#"nameString is: %#", nameString);
If my understanding is correct (which it very well may not be), then nameString is defined to be a pointer to a String. I thought then that this would print the memory address that nameString holds, not the value of that address. So, if that is true, then in the NSLog statement, to get the value of the pointer, shouldn't we need to use the asterisk notation to access what nameString points to like this:
NSLog(#"nameString is: %#", *nameString);
?
It has been a little while since programming in C, but since Objective-C is a superset of C I thought they would behave similarly.
An explanation would be greatly appreciated! Thanks!
The command %# is like "shortcut" that calls the method -description on the receiver. For an NSString it simply display the string itself, since is inherited from NSObject you can override it, very usefull if you create for own class. In that case the default behaviur is print the value of the pointer. If you want to print the address of the pointer in the string just replace with :
NSLog(#"nameString is: %p", nameString)
I think that you use an asterisk only to declare a pointer. Then, you only use the name you decided. For example:
NSString *foo = [[NSString alloc] initWithString:#"Hello"];
NSLog(#"%#", foo);
Correct me if I am wrong :)
It's an object and NSLog is a function that uses its format specifiers to determine what to do with the argument. In this case the specifier is %# which tells NSLog to call a method on an object.
Normally this will call the method "description" which returns an NSString but it probably does respondsToMethod first and falls through to some other string methods.

adding Key values from NSDictionary to NSarray

I want to add all the keys from the dictionary to the array. This is what I am doing right now.As code below:
for (NSString * akey in _groups ) {
NSLog(#"%#",akey);
[_groupArray addObject:akey];
NSLog(#"%#",_groupArray);
}
The log is showing Null for _groupArray. I even tried using insertObjectAtIndex even that does not work.Not sure what I am doing wrong and yes I am getting the keys in the dictionary (_groups) nothing wrong with that.
You should initialize the array before starting to add values to it. Otherwise it is initially nil and will remain nil.
You can use allKeys to get all the keys of the array. But since _groupArray is an NSMutableArray, you have to do that like this:
_groupArray = [NSMutableArray arrayWithArray:[_groups allKeys]];
Or:
_groupArray = [[_groups allKeys] mutableCopy];
If _groupsArray is new or empty anyway then you can use
_gropusArray = [NSMutableArray arrayWithArray:[dict allKeys]];
That should release you from the compiler warning.
If _groupsArray was not empty before and you need to addValues then go for:
[_groupsArray addObjectsFromArray:[dict allKeys]];
this is how you should do it....
_groupsArray=[dict allKeys];
don't forget to allocate memory to your _groupsArray.. :)
Have you checked you've alloc'd and init'd your mutable array. Unless there's already stuff in it that you're adding to - and even then - consider using the NSDictiomary allKeys method rather than iterating through the dictionary.
From the docs
allKeys
Returns a new array containing the dictionary’s keys.
- (NSArray *) allKeys
Return Value
A new array containing the dictionary’s keys, or an empty array if the dictionary has no entries.
Discussion
The order of the elements in the array is not defined.

NSUserDefaults: How to display user defaults when valueforKey is an ENUM

In iPhone app, How to display values in console for user defaults when valueforKey is an ENUM?
Currently with the below code if I try to display in console then it crashes with no crash log in console.
NSLog(#"%#",[[NSUserDefaults standardUserDefaults] valueForKey:Enum]);
To fetch:
int someValue = [[NSUserDefaults standardDefaults] integerForKey:#"your key here"];
to save:
[[NSUserDefaults standardDefaults] setInteger:10 forKey:#"your key here"];
EDIT: got it, you crash because in NSLog you are using the wrong format:
NSLog(#"%i",[[NSUserDefaults standardUserDefaults] valueForKey:Enum]);
try %i (to print integers) instead of %# (used to print valid objective-c objects)
Since an enum is really just a fancy way of displaying an int, all you have to do is create an enum variable and set it to the value you get from NSUserDefaults.
You may end up with something like:
PirateEnumType pirateType = [[NSUserDefaults standardUserDefaults] integerForKey:#"PirateType"];
EDIT:
I'm sorry. I guess I didn't fully understand your question. Since an enum is really an int, you will need to use "%i" to display it instead of "%#".
In order to show it properly as a human readable string, you would need to have some sort of enumToString function, perhaps like:
-(NSString*)enumToString:(PirateEnumType)enumValue {
NSString* returnValue = #"";
switch (enumValue) {
case Captain:
returnValue = #"Captain";
break;
case Swashbuckler:
returnValue = #"Swashbuckler";
break;
case PegLeg:
returnValue = #"PegLeg";
break;
}
return returnValue;
}
NSUserDefaults objects are really just NSDictionary objects. NSDictionary only takes an object as a key. Enum is just an integer, so you can't use it as a key directly. One way to do it, though is to use NSNumber, which can represent an integer as an object like the following:
[[NSUserDefaults standardDefults] valueForKey:[NSNumber numberWithInt:yourEnum];
To set the value you would do:
[[NSUserDefaults standardDefaults] setValue:yourValue forKey:[NSNumber numberWithInt:yourEnum]];
There are two issues here. Firstly, an enum cannot be a key in valueForKey. valueForKey can only accept strings, not string values of enums. Therefore, you need to implement a method to get a string value from an enum. In my example below that method is stringFromEnum. Here is one example of how to get a string from an enum.
Sometimes I've seen NSLog choke when I ask it to directly include the return value of a message sent to an object. I've always found that assign the result of that message to an instance variable first and then passing the value of that instance variable into NSLog instead solved the problem, but admit I haven't figured out what the relevant factors are that determine when it does or does not work the original way. Interested in any comments.
NSString *myKey = [self stringFromEnum:Enum];
NSString *myStringToLog = [[NSUserDefaults standardUserDefaults] valueForKey:myKey];
NSLog(#"%#", myStringToLog);
Another reading of your question could be that your instance variable Enum is actually a string, which is the Key for an enum that will be returned from NSUserDefaults. However, that can't be the case, as NSUserDefaults will not store an enum. See the documentation for object types that are supported. If that's what you're trying to do, your problem may be partly in other code where you're trying to store the enum in the first place?

NSDictionary with integer as number

I have a NSDictionary with the following layout:
{
1:{
... some data ...
}
...
}
I have a NSNumber object with a integer value of 1, but when I do
[my_dict objectForKey:my_number] it returns null.
If I try and convert NSNumber to a integer via [my dict objectForKey:[my_number intValue]] I get a warning and the program crashes when it reaches that part of the code.
Any help would be greatly appreciated.
Keys in a NSDictionary or NSMutableDictionary must be objects, like NSNumber. They cannot be primitive data types, like int.
http://developer.apple.com/library/ios/#documentation/Cocoa/Reference/Foundation/Classes/NSDictionary_Class/Reference/Reference.html
Looks like you're trying to use an integer as the key in your NSDictionary. This would be correct with an NSArray, with an NSDictionary actually needs a proper object as a key.
You might have more success in this particular case feeding that data into an NSArray, and accessing it with:
id *someData = [my_array objectAtIndex:1];

How to search through a NSMutableArray

I have a NSMutableArray that I need to search for a string and return the key in the array where the string was found. So for example if I'm searching "ipod" and it's the 4th in the array, it would return 3 or whatever position the string is in. What's the best way to do this?
return [theArray indexOfObject:#"ipod"];
Reference: http://developer.apple.com/iphone/library/documentation/Cocoa/Reference/Foundation/Classes/NSArray_Class/NSArray.html#//apple_ref/occ/instm/NSArray/indexOfObject:.
Note that NSMutableArray inherits from NSArray, so any NSArray methods can be used on NSMutableArray too.
Again from the documentation:
Index of Object Passing test
You'll have to write a code block that tests for the substring in each object: NSString rangeOfString: options:
Then you'll get the index of the object with the substring. You'll need to run the string search again for your result, but that should get you what you are after.