Getting values from userdefaults and creating an NSMutableDictionary - ios

I am trying to create a dictionary which is a replica for NSUserdefaults. I want the dictionary to contain same values and keys.
But, we need to convert bool and int values to NSNumber when we save it to dictionary. Right now i am doing the following. But not sure which value is bool value and int value. If I can get to know the type of the value I can do rest. Is there any way to check the value whether it is bool or int.
NSArray *availableUserDefaultsKeys = [NSArray arrayWithObjects:#"Key1", #"Key2",nil];
NSMutableDictionary *userDefaultsDictionary = [NSMutableDictionary dictionary];
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
for (NSString *key in availableUserDefaultsKeys) {
id value = [userDefaults objectForKey:key];
if (value != nil) { // Is there any way to check whether the value is bool or int here
[userDefaultsDictionary setObject:value forKey:key];
} else {
[userDefaultsDictionary setObject:[NSNull null] forKey:key];
}
}
I have checked the debug.plist which has all the user defaults stored, In that we have type field where it specifies the type. Can we get the type from this field.

You can use this way:
// v is NSNumber
if ((strcmp([v objCType], #encode(BOOL) == 0) {
// this is BOOL
}
And there are #encode(int) #encode(float)
You should avoid directly saving seperated data to NSUserDefaults, you can pack them in an object, and this object should conform NSCoding protocol. Then after you read the data out, you will know the exact type.

Related

Problems with NSMutableDictionary and NSMutableArray ( Mutating Error - but why? ) [duplicate]

The following code is returning an exception with the following error message "mutating method sent to immutable object" when attempting to removeObjectForKey
NSMutableDictionary * storedIpDictionary = (NSMutableDictionary*)[[NSUserDefaults standardUserDefaults] dictionaryForKey:#"dictDeviceIp"];
NSString *key = self.currentDeviceNameText.text;
NSString *ipAddressTemp = [storedIpDictionary objectForKey:key];
[storedIpDictionary removeObjectForKey:key]; <----Crashes here
storedIpDictionary[key] = ipAddressTemp;
Not sure what the issue is, perhaps it is due to retrieving the dictionary from a NSUserDefaults.
However the following code works without any issues.
NSMutableDictionary * storedIpDictionary = (NSMutableDictionary*)[[NSUserDefaults standardUserDefaults] dictionaryForKey:#"dictDeviceIp"];
[storedIpDictionary removeAllObjects];
NSUserDefaults returns immutable objects, even if you put in mutable ones. You must call -mutableCopy on the returned value to get a mutable collection.
You cant just cast an NSDictionary to NSMutableDictinary thats not at all how casting works.
to remove a key from NSUserDefualts call removeObjectForKey on the NSUserDefaults instance itself.
if you really do want a dictionary for some other reason, then you must make a mutableCopy from the dictionary obtained by dictionaryForKey.
This is the code that eventually worked, I used some of the details provided from others above, but none had it completely explained.
- (void)cleanDictionary
{
NSMutableDictionary * storedIpDictionary = [[[NSUserDefaults standardUserDefaults] objectForKey: #"dictDeviceIp"] mutableCopy];
[[NSUserDefaults standardUserDefaults] removeObjectForKey:#"dictDeviceIp"];
NSString *oldKey = self.currentDeviceNameText.text;
NSString *newKey = self.deviceNameChangeText.text;
NSString *ipAddressTemp = [storedIpDictionary objectForKey:oldKey];
// Make some change to the structure
[storedIpDictionary removeObjectForKey:oldKey]; // Remove object
storedIpDictionary[newKey] = ipAddressTemp; // Add object with new key
// Add it the whole thing back into NSUserDefaults
[[NSUserDefaults standardUserDefaults] setObject:storedIpDictionary forKey:#"dictDeviceIp"];
// Synchronize to ensure it's saved
[[NSUserDefaults standardUserDefaults] synchronize];
}
if you have on error NSMutableDictionary: mutating method sent to immutable object in Swift, make this step:
This is because you have assigned a NSUserDefault to NSMutableArray, when you take something NSUserDefault it returns you a NSArray not a NSMutableArray, so in this case you have to use a NSMutableArray Auxiliary .
see for Swift :
var Products:NSMutableArray = NSMutableArray()
override func viewDidAppear(animated: Bool) {
if let Produtos = NSUserDefaults.standardUserDefaults().valueForKey("Produtos") {
Products = Produtos as! NSMutableArray
}
}
func InsertProducts(productCode:String){
//COPY Products Atual for auxMutable
var auxMutable = Products.mutableCopy()
//Add object in auxMutable
auxMutable.addObjectsFromArray([productCode])
//in line back data to Array Products and make cast to NSMutableArray
Products = auxMutable as! NSMutableArray
//Refresh Data of NSUserDefaults
NSUserDefaults.standardUserDefaults().setObject(Products, forKey: "Produtos")
}
#IBAction func Bt_New_Product(sender: AnyObject) {
var ProductName:String = TXT_NameProduct.text
InsertProducts(ProductName)
}
This work for me!!!
i found same issue and found solution hope it will help some one.
arrayOfferId = defaults.objectForKey("offerId")?.mutableCopy() as! NSMutableArray
NSUserDefaults returns immutable objects, even if you put in mutable ones. You must call -mutableCopy on the returned value to get a mutable collection. so when you get value from NSUserDefault use mutableCopy()
[NSUserDefaults dictionaryForKey] returns an immutable dictionary (NSDictionary) and you cannot force it to be mutable by casting it to NSMutableDictionary.
Instead you must create the mutable dictionary using mutableCopy, overwrite the element and then re-assigning the dictionary back into NSUserDefaults:
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
NSMutableDictionary *storedIpDictionary = [[userDefaults dictionaryForKey:#"dictDeviceIp"] mutableCopy];
NSString *key = self.currentDeviceNameText.text;
NSString *ipAddressTemp = [storedIpDictionary objectForKey:key];
// Don't need this line
//[storedIpDictionary removeObjectForKey:key];
storedIpDictionary[key] = ipAddressTemp;
[userDefaults setObject:storedIpDictionary
forKey:#"dictDeviceIp"];

How to tell the difference between a Boolean and an NSNumber in CoreData

I have some parsing code I'm using for serialising and deserialising objects from our web service and I've hit a bit of a problem when serialising booleans.
The serialisation looks like this:
- (NSDictionary *)dictionaryRepresentationWithMapping:(NSDictionary *)mappingDictionary
{
NSMutableDictionary *dictionary = [[NSMutableDictionary alloc]init];
for (id key in[mappingDictionary allKeys])
{
id value = [self valueForKey:key];
if ((value != [NSNull null]) && (![value isKindOfClass:[NSNull class]]) && (value != nil))
{
[dictionary setObject:value forKey:mappingDictionary[key]];
}
}
return [NSDictionary dictionaryWithDictionary:dictionary];
}
The problem is that when I call valueForKey: on my NSManagedObject and then add this to my dictionary I end up with the value being set as if I was calling:
[dictionary setObject:#1 forKey:mappingDictionary[key]];
instead of:
[dictionary setObject:#YES forKey:mappingDictionary[key]];
This means that when I turn this into JSON, in the next stage, I'm sending 1 instead of true to the server.
So what I need is a way of retaining the fact that this is an NSNumber representing a bool as opposed to a number. I've tried asking for the class but I just get back NSNumber. Is there a way I can retain this automatically or failing that, is there a way I can consult the model to see what the attribute type was set to?
Each entity has its metadata stored in NSEntityDescription and NSAttributeDescription. You can access them from NSManagedObject in a following way:
//you can put this inside the for loop
NSAttributeDescription *attributeDescription = self.entity.attributesByName[key];
if(attributeDescription.attributeType == NSBooleanAttributeType) {
//it is a boolean attribute
}
When sending a call to the server, you could do like this:
[dict setValue:[NSNumber numberWithBool:YES] forKey:mappingDictionary[key]]; ;
Or another way, you can model server side to retain its value as Boolean, and at that time, just need to send like this [dict setValue:YES] forKey:mappingDictionary[key]];
Hope it could help

How to check NSDictionary values are nil in iPhone

I have one NSDictionary and in that dictionary keys and values are dynamically added.Now I want to check if any value is nil then set empty string for that key.How can I check this? Now I am checking this using conditions but, Is there any simple way to check nil or empty values.Please help me,Thanks
NSDictionary does not hold nil values for its keys.
If you want to test the presence/absence of a specific key's value, you can use -objectForKey:. The method returns nil if the key/value pair is absent.
For "empty values", you will have to expand on what is considered an empty value in the context of your program (e.g. some people use NSNull to indicate an empty value, or if you are working with strings as values an empty string).
For any key you haven’t set a value for, your dictionary will return nil from -objectForKey:—in other words, if nothing’s set for a given key, that key doesn’t exist in the dictionary.
That said, it sounds like you have a list of keys that you’d like to ensure have a placeholder value in the dictionary, with that placeholder value being an empty string. For that, you’d want to do something like this:
- (void)addPlaceholdersForKeys:(NSArray *)keys toDictionary:(NSMutableDictionary *)dictionary {
for (id key in keys) {
if ([dictionary objectForKey:key] == nil) {
[dictionary setObject:#"" forKey:key];
}
}
}
NSDictionary and other collections cannot contain nil values. But it must store a null value. You can check it like that:-
if (dictionary[key] == [NSNull null]) {
[dictionary setObject:#"" forKey:key];
}
For more refer sample code:-
NSMutableDictionary *dict=[NSMutableDictionary dictionary];
[dict setObject:#"test" forKey:#"key1"];
[dict setObject:#"test1" forKey:#"key2"];
[dict setObject:[NSNull null] forKey:#"key3"];
NSLog(#"%#",dict);
if (dict[#"key3"]==[NSNull null])
{
[dict setObject:#"" forKey:#"key3"];
NSLog(#"%#",dict);
}
Output:-
Before setting empty value:-
{
key1 = test;
key2 = test1;
key3 = "<null>";
}
After setting empty value:-
{
key1 = test;
key2 = test1;
key3 = "";
}
you can do like this by looping through the dictionary
NSArray *keys = [dictionary allKeys];
for(NSString *key in keys)
{
if ([dictionary objectForKey:key] == NULL)
{
[dictionary setObject:#"" forKey:key];
}
}

Storing Multiple Values to a Single Key in NSDictionary

In my application i am getting data from the server.i parsed the data and added to individual arrays. Here i am having 2 arrays.
For example
Array A : #"1",#"2",#"3",#"2",#"3",#"4",etc..
Array B : #"A",#"B",#"C",#"D",#"E",#"F",etc..
Now i want to create a Dictionary with Array A as keys and Array B as Values.
i am trying to create Dictionary like this:
dataDict = [NSDictionary dictionaryWithObjects:B forKeys:A];
But it is giving only single value for a single Key. here how can i store multiple values for a single key.
For Different keys its working. But my problem is Storing multiple values for single key.
You can't store multiple values for a single key directly -- dictionaries can only have one value per key. What you can do is store an array as the value. So, you could create a mutable dictionary and add the keys and values one at a time. Make the values all mutable arrays, and check for an existing value for the given key before setting it. If you find one, add the new value to the array.
Try this,
Assuming dataDict is a NSMutableDictionary and initialised.
- (void)addValueInDataDict:(id)value forKey:(NSString *)key {
if ([dataDict objectForKey:key] != nil) {
//Already exist a value for the key
id object = [dataDict objectForKey:key];
NSMutableArray *objectArray;
if ([object isKindOfClass:[NSMutableArray class]]) {
objectArray = (NSMutableArray *)object;
} else {
NSMutableArray *objectArray = [[NSMutableArray alloc] init];
}
[objectArray addObject:value];
[dataDict setObject:objectArray forKey:key];
} else {
//No value for the key
[dataDict setObject:value forKey:key];
}
}

Saving NSdictionary in NSUserDefaults

I tried to save an NSDictionary in NSUSerDefaults, but I get the following error:
Attempt to insert non-property value
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
if(defaults) {
[defaults setBool: YES forKey: #"disableGetStarted"];
[defaults setObject: [json mutableCopy] forKey: #"user"];
[defaults synchronize];
NSLog(#"defaults %#", [defaults objectForKey: #"user"]);
}
Where json is an NSDictionary.
What can I do?
json may be a dictionary but all of the contents of the dictionary must be legal values to be stored in user defaults. All must be instances of: NSData, NSString, NSNumber, NSDate, NSArray, or NSDictionary.
If you only store standard objects inside the dictionary like NSData, NSString, NSNumber, NSDate, NSArray, NSDictionary or a combination of them you don't have to do anything special.
However, if you have instances of custom objects in it (i.e. classes that you've created) you first need to convert it into a compatible type (e.g. NSData). You can do this using the code below:
NSData* data=[NSKeyedArchiver archivedDataWithRootObject:json];
[NSUserDefaults standardUserDefaults] setObject:data forKey:#"user"]
For this method to work, you ned to implement these 2 methods in the custom classes you are trying to save:
- (id)initWithCoder:(NSCoder*)coder
- (void)encodeWithCoder:(NSCoder*)coder;
To get the dictionary back from NSUserDefaults (decode) you can use:
NSData* data = [[NSUserDefaults standardUserDefaults] objectForKey:#"user"];
NSDictionary* json = [NSKeyedUnarchiver unarchiveObjectWithData:data];
EDIT
To check if your json object contains any [NSNull null] values, add this piece of code before you are making your insert into NSUserDefaults
for (id val in [json allValues])
{
if ([val isKindOfClass:[NSNull class]])
{
NSLog(#"This bad! NSNull should not be in the dictionary");
break;
}
}
If you get any This is bad... messages in the console, then you have 2 choices.
1. Use the archiving/unarchiving method I described above
2. Replace the NSNull objects from the dictionary with other values (e.g. empty strings) if this does not break your code.
One or more of the objects in your dictionary are not NSString, NSArray, NSDictionary, NSDate or NSData, the only objects that may be present. This code may help, it gives you more details why your dictionary is not valid:
- (BOOL)isValidPropertyList:(id)object {
//
// Check if `object` is a valid property list.
//
// # Return Value
// `YES` if the receiver is a valid property list, `NO` otherwise.
//
// # Discussion
// Intended to be called on NSArray and NSDictionary object to verify if the
// receiver is a valid property list. Will emit a description of any
// detected error.
//
NSData *xmlData;
NSString *error;
xmlData=[NSPropertyListSerialization
dataFromPropertyList:object
format:NSPropertyListXMLFormat_v1_0
errorDescription:&error
];
if(xmlData)
{
return YES;
}
else
{
NSLog(#"Tested object is not a valid property list: %#",error);
}
return NO;
}
NSDictionary *dict = [[NSDictionary alloc] init];
dict = json;
[NSUserDefaults standardDefaults] setObject:dict forKey:#"user"];
NSUserDefaults doesn't distinguish between mutable and immutable objects, so when you get it back it'll be immutable. So if you make a mutable dictionary by chance ->
[[NSUserDefaults standardDefaults] setObject:dict forKey:#"user"] mutableCopy];
All objects of the dictionary json must be instances of: NSData, NSString, NSNumber, NSDate, NSArray, or NSDictionary.

Resources