How to store/retrieve NSDictionary into NSUserDefaults? - objective-c

in my app I have no problem withs saving/retrieving string values(like myTextField.text) into NSUserDefaults but whatever i do to store NSDictionary i coldn't succeed. And i tried a lot of answer from this site+ Google. Can anyone please help? Here is my code too:
id result=[NSJSONSerialization JSONObjectWithData:data options:
NSJSONReadingMutableContainers error:&error];
NSLog(#"Result: %#", result);
NSDictionary *dict= [result objectForKey:#"message"];
//i want to store dict sub dictionary
*EDIT*Note:The dictionary contains a kind of array of objects, and the objects are all members of property list(NSString, NSDate, ...)

What the other answers are trying to tell you is, you have to get rid of your (id). NSUserDefaults and the compiler will complain and throw warnings if you try and put an id into the user defaults. Try it out this way.
NSDictionary *result = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&error];
if (result) {
[[NSUserDefaults standardUserDefaults] setObject:result forKey:#"yourUniqueKey"];
}

This is because some of the objects in your NSDictionary are not Objective C primitive objects, or to put it a better way, iOS doesn't know how to convert the custom data in the NSDictionary to a form that can be saved and loaded again.
So you need to learn how to get those custom objects to conform to NSCoding.
Here is a related question that should have some useful information for you.

It depends on what you are storing. Apple's documentation says
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.
I assume you are trying to store a type other than the listed ones.

Related

Transform NSCFDictionary to NSDictionary [duplicate]

I'm getting an NSCFDictionary returned to me and I can't figure out how to use it. I know it's of type NSCFDictionary because I printed the class and it came out as __NCSFDictionary. I can't figure out how to do anything with it.
I'm just trying to hold onto it for now but can't even get that to work:
NSDictionary *dict = [[NSURLCredentialStorage sharedCredentialStorage] allCredentials];
for(NSURLProtectionSpace key in [dict keyEnumerator])
{
NSCFDictionary *value = [dict objectForKey:key];
}
The class reference for allCredentials says its supposed to return a dictionary whose values are also dictionaries. My assignment statement isn't working though. Do I need a cast of some kind?
NSDictionary and the other collection classes are actually class clusters: several concrete subclasses classes masquerading under the interface of a single class: they all provide the same functionality (because they are subclasses of the same class — in NSDictionary's case, this involves the three "primitive methods" -count, -objectForKey:, and -keyEnumerator), but have different internal workings to be efficient in different situations, based on how they're created and what type of data they may be storing.
NSCFDictionary is simply a concrete subclass of NSDictionary. That is, your NSDictionaries may actually be NSCFDictionary instances, but you should treat them as instances of NSDictionary, because that will provide you with the required dictionary-storage functionality.
NSDictionary *value = [dict objectForKey:key];
Now, another reason your code doesn't work: NSURLProtectionSpace is a class, so you should use it as a pointer, like this:
for (NSURLProtectionSpace *key ...
NSCFDictionary is the private subclass of NSDictionary that implements the actual functionality. It's just an NSDictionary. Just about any NSDictionary you use will be an NSCFDictionary under the hood. It doesn't matter to you code. You can type the variable as NSDictionary and use it accordingly.
I have an NSCFDictionary that is actually a NSMutableDictionary object, I can delete items from it. I mention this to further clarify jtbandes' answer: the NSCFDictionary object may be any object that inherits from NSDictionary.

nsnumber into uipasteboard

I'd like to copy a float into the pasteboard, but the important thing is the value, as I want to paste it later in numbers, as a number.
Tried with :
[pasteboard setValue:SomeNSNumberWhereIStoredTheFloat forPasteboardType:#"NSNumber"];
With that, it got nothing to paste, and with pasteboard.string = numberInStringValue, it pastes the number as a series of characters, in what I'm not interested.
Thanks for your help
The "type" of pasteboard data is not the name of a class, it's a Uniform Type Identifier (UTI, or just UT if you remember what else UTI stands for.) In this case, your data does not have an associated UTI (numbers are abstract concepts, not data formats.) You'll have to figure out the best way to store that number and retrieve it.
I think in this case, formatting the number into a string will suffice:
NSString *numString = [NSString stringWithFormat:#"%f", theFloatValue];
pasteboard.string = numString;
And later, when getting it back:
float theFloatValue2 = [pasteboard.string doubleValue];
This does not take into account checking for nil or other error handling.
If you need very high precision, you may need to investigate an NSData-based storage technique.
You can store an NSNumber directly. You can used the following methods from the API
setValue:forPasteboardType:
Use this method to put an object on the pasteboard that is a standard property-list object that is an object of the NSString, NSArray, NSDictionary, NSDate, NSNumber, or NSURL class.
valueForPasteboardType:
This method attempts to return an object that is of a class type appropriate to the representation type, which typically is a UTI. For example, if the representation type is kUTTypePlainText (public.plain-text), the method returns an NSString object. If the method cannot determine the class type from the representation type, it returns the object as a generic property-list object. Property-list objects include NSString, NSArray, NSDictionary, NSDate, or NSNumber objects, with NSURL objects also as a possibility. If the method cannot decode the value as a property-list object, it returns the pasteboard item as an NSData object.
The real problem comes in finding the correct UTI so the class will automatically give you back an NSNumber and not give you back an NSData object instead.
To make matters worse, the code doest not appear to work as the advertised by the documentation. I've heard from several people the method will always return you NSData. You can find an example (and a workaround) of such issue in this answer.
You can store your float value as NSNumber.
But NSNumber is not stored in UIPasteboard correctly although docs states it does (bug?).
To keep NSNumber in UIPasteboard you should archive NSNumber to NSData, and to retrieve NSNumber from UIPasteboard you should unarchive NSData back to NSNumber.
// adding data to pasteboard
NSNumber *number = [NSNumber numberWithDouble:floatValue]; // store your value here
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:number]; // archive NSNumber to NSData
NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:data, #"yourKey",nil];
[[UIPasteboard generalPasteboard] addItems:[NSArray arrayWithObject:dict]];
// retrieving data
NSData *data = [dict valueForKey:#"yourKey"]; // here dict is properly obtained NSDictionary of pasteboard object
NSNumber *number = [NSKeyedUnarchiver unarchiveObjectWithData:data]; // unarchive NSData to NSNumber

How do I convert a JSON structure to an object in Objective-C?

I have a JSON structure like this:
{
id:100
contactInfo: {
name: John Doe
city: New York
}
}
and a corresponding Customer class w/ the following properties including a nested contactInfo class.
Customer.id
Customer.contactInfo.name
Customer.contactInfo.city
Is it possible to convert the JSON data directly to an instance of the Customer class?
I know how to get an NSDictionary object, but that is very cumbersome and verbose to work with and would rather convert/deserialize the data directly to the Customer class.
EDITED w/ Additional Info:
In other languages I have worked with there is built-in support for deserialization of JSON to custom objects. All you have to do is mark the class as "Serializable" and the JSON is deserialized w/o the need to write custom code in each class.
The NSDictionary object can become very cumbersome if the class has nested classes. It would be much easier to use the custom object properties like this
Customer.contactInfo.name
rather than
[(NSDictionary*)[customerDict objectForKey:#"contactInfo"] objectForKey:#"name"]
thus my question about a standard built-in deserializer in Objective-C.
As of iOS5 there if official support for JSON
https://developer.apple.com/library/ios/#documentation/Foundation/Reference/NSJSONSerialization_Class/Reference/Reference.html
I don't know why you think it is cumbersome to work with. A simple way to do it would be to have an initialiser for your customer object that took a dictionary as a parameter.
That way you could get your downloaded data as JSON, deserialise it into a dictionary and then pass it to the initializer to create an object from the dictionary.
Any of the dozens of JSON libraries will parse your JSON string and turn it into an NSDictionary. You're going to have to deal with an intermediate format if you don't want to write a parser yourself. But, once you have the NSDictionary you can use Key Value Coding (KVC) to set the properties on your object:
for (NSString *key in jsonDictionary) {
[customer setValue:[jsonDictionary objectForKey] forKey:key];
}
KVC is built into Cocoa, and defines setValue:forKey: for all objects, so you can set properties by name regardless of how they are defined.
You'll have to do something smarter than the loop above to handle your internal ContactInfo class, but you should be able to figure that out. For safety (since the JSON is probably coming over the network) you might want to make a whitelist of keys and loop over that, and use valueForKeyPath:/setValue:forKeyPath: to handle nested objects.
Consider using RestKit api (http://restkit.org/) and its object mapping system...
Here is some wiki : https://github.com/RestKit/RestKit/wiki/Object-mapping
Write an initializer for your Customer class that takes JSON data as a parameter, something like:
-(id)initWithJSONData:(NSData*)data;
You can implement that method to do what Abizern suggests: read the data into a dictionary, and then pull whatever values you need out of the dictionary.
If you want to get fancy, another way you could go is to adopt NSCoding in your Customer class, and then create a NSCoder subclass that deserializes JSON data. This could work very much like NSKeyedUnarchiver. Doesn't seem worth the trouble unless you're going to take the same approach with a number of your classes, though.
Using this library, you can do the following:
SBJSON *parser = [[SBJSON alloc] init];
NSDictionary *customer = [parser objectWithString:jsonString];
You can then access your properties using:
[customer objectForKey:#"id"];
[[customer objectForKey:#"contactInfo"] objectForKey:#"name"];
[[customer objectForKey:#"contactInfo"] objectForKey:#"city"];
You can then use this code in your Customer class' init function. For example:
- (id)initWithJSON:(NSString *)jsonString {
self = [super init]; // or whatever you need here
if (self) {
SBJSON *parser = [[SBJSON alloc] init];
NSDictionary *customer = [parser objectWithString:jsonString];
self.id = [customer objectForKey:#"id"];
self.contactInfo = [[NSDictionary alloc] initWithObjectsAndKeys:[[customer objectforKey:#"contactInfo"] objectForKey:#"name"], #"name", [[customer objectforKey:#"contactInfo"] objectForKey:#"city"], #"city"]
}
return self;
}

What is an NSCFDictionary?

I'm getting an NSCFDictionary returned to me and I can't figure out how to use it. I know it's of type NSCFDictionary because I printed the class and it came out as __NCSFDictionary. I can't figure out how to do anything with it.
I'm just trying to hold onto it for now but can't even get that to work:
NSDictionary *dict = [[NSURLCredentialStorage sharedCredentialStorage] allCredentials];
for(NSURLProtectionSpace key in [dict keyEnumerator])
{
NSCFDictionary *value = [dict objectForKey:key];
}
The class reference for allCredentials says its supposed to return a dictionary whose values are also dictionaries. My assignment statement isn't working though. Do I need a cast of some kind?
NSDictionary and the other collection classes are actually class clusters: several concrete subclasses classes masquerading under the interface of a single class: they all provide the same functionality (because they are subclasses of the same class — in NSDictionary's case, this involves the three "primitive methods" -count, -objectForKey:, and -keyEnumerator), but have different internal workings to be efficient in different situations, based on how they're created and what type of data they may be storing.
NSCFDictionary is simply a concrete subclass of NSDictionary. That is, your NSDictionaries may actually be NSCFDictionary instances, but you should treat them as instances of NSDictionary, because that will provide you with the required dictionary-storage functionality.
NSDictionary *value = [dict objectForKey:key];
Now, another reason your code doesn't work: NSURLProtectionSpace is a class, so you should use it as a pointer, like this:
for (NSURLProtectionSpace *key ...
NSCFDictionary is the private subclass of NSDictionary that implements the actual functionality. It's just an NSDictionary. Just about any NSDictionary you use will be an NSCFDictionary under the hood. It doesn't matter to you code. You can type the variable as NSDictionary and use it accordingly.
I have an NSCFDictionary that is actually a NSMutableDictionary object, I can delete items from it. I mention this to further clarify jtbandes' answer: the NSCFDictionary object may be any object that inherits from NSDictionary.

Change the values within NSArray by dereferencing?

I've come across a problem related to pointers within arrays in objective-c.
What I'm trying to do is take the pointers within an NSArray, pass them to a method, and then assign the returned value back to the original pointer(the pointer which belongs to the array).
Based on what I know from C and C++, by dereferencing the pointers within the array, I should be able to change the values they point to... Here is the code I'm using, but it is not working (the value phone points to never changes based on the NSLog output).
NSArray *phoneNumbers = [phoneEmailDict objectForKey:#"phone"];
for (NSString* phone in phoneNumbers) {
(*phone) = (*[self removeNonNumbers:phone]);
NSLog(#"phone:%#", phone);
}
And here is the method signature I am passing the NSString* to:
- (NSString*) removeNonNumbers: (NSString*) string;
As you can see, I am iterating through each NSString* within phoneNumbers with the variable phone. I pass the phone to removeNonNumbers:, which returns the modified NSString*. I Then dereference the pointer returned from removeNonNumber and assign the value to phone.
As you can tell, I probably do not understand Objective-C objects that well. I'm pretty sure this would work in C++ or C, but I can't see why it doesn't work here! Thanks in advance for your help!
Yeah, that's not going to work. You'll need an NSMutableArray:
NSMutableArray * phoneNumbers = [[phoneEmailDict objectForKey:#"phone"] mutableCopy];
for (NSUInteger i = 0; i < [phoneNumber count]; ++i) {
NSString * phone = [phoneNumbers objectAtIndex:i];
phone = [self removeNonNumbers:phone];
[phoneNumbers replaceObjectAtIndex:i withObject:phone];
}
[phoneEmailDict setObject:phoneNumbers forKey:#"phone"];
[phoneNumbers release];
You can't dereference Objective-C object variables. They are always pointers, but you should treat them as though they're atomic values. You need to mutate the array itself to contain the new objects you're generating.
NSArray is not a C/C++ style array. It's an Objective-C object. You need to use the instance methods of the NSArray class to perform operations on it.
In Objective-C you never "dereference" an object pointer to set its value.
Also, you're using what is called Fast Enumeration, which does not allow mutation.
You can also use enumerateObjectsUsingBlock:.
NSArray *array = [NSArray array];
__block NSMutableArray *mutableCopyArray = [array mutableCopy];
[mutableCopyArray enumerateObjectsUsingBlock:^(id object, NSUInteger idx, BOOL *stop) {
[mutableCopyArray replaceObjectAtIndex:idx withObject:[object modifiedObject]];
}];
Checkout How do I iterate over an NSArray?
While this may work to some degree, I haven't tested it, I'd file this under 'bad idea' and not touch. NSArray, and many other cocoa objects, a fairly complex and can have a variety of implementations under the hood as part of the class cluster design pattern.
So when it comes down to it you really won't know what you're dealing internally. NSArray is actually designed to be immutable so in place editing is even doubly a bad idea.
Objects that are designed to let you mess around with the internals expose those through api methods like NSMutableData's mutableBytes.
You're better off constructing a new NS(Mutable)Array with the processed values.