This question already has answers here:
Convert any Data Type into NSData and back again
(1 answer)
NSData on custom class?
(1 answer)
Closed 6 years ago.
I'm trying to transfer data between 2 devices using bluetooth. I want to convert custom NSObject to NSData.
What is the best way to decode the received NSData into a custom NSObject ?
Thanks!
You have to use NSCoding. NSCoding is a simple protocol, with two methods: initWithCoder: and encodeWithCoder:. Classes that conform to NSCoding can be serialized and deserialized into data that can be either be archived to disk or distributed across a network.
NSCoding / NSKeyed​Archiver.
Here is tutorial of NSCoding.
Archiving
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:YourClass];
Unarchiving
YourClass *objYourClass = [NSKeyedUnarchiver unarchiveObjectWithData:data];
You can also refer my answer. First you have to create Bean Class and implement initWithCoder: and encodeWithCoder: after that you can Archive NSData from bean class object and Unarchive bean class object from NSData.
My very simple answer
First we have to implement the encodeWithCoder and initWithCoder in NSObject.m and before that I have 2 data(username and password) in NSObject Class.For example I set this.You can understand
- (void)encodeWithCoder:(NSCoder *)encoder
{
//Encode properties, other class variables, etc
[encoder encodeObject:#"Dev" forKey:#"username"];
[encoder encodeObject:#"Test#123" forKey:#"password"];
}
- (id)initWithCoder:(NSCoder *)decoder
{
if((self = [super init]))
{
//decode properties, other class vars
userName = [decoder decodeObjectForKey:#"username"];
passWord = [decoder decodeObjectForKey:#"password"];
}
return self;
}
Then in ViewController.m
For Save
NSObjectClass *className = [[NSObjectClass alloc]init];
NSUserDefaults *currentDefaults = [NSUserDefaults standardUserDefaults];
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:className];
[currentDefaults setObject:data forKey:#"DATA"];
[currentDefaults synchronize];
For Retrieve
NSData *data = [currentDefaults objectForKey:#"DATA"];
className = [NSKeyedUnarchiver unarchiveObjectWithData:data];
For More Details Go through My ANSWER
Related
PFQuery *Location = [PFQuery queryWithClassName:#"Location"];
[Location findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
NSLog(#"%#", [objects objectAtIndex:0]);
}];
How can i store this object in NSUserDefaults?
For storing custom objects u need to add these two methods in your m file of custom object class
-(void)encodeWithCoder:(NSCoder *)encoder
{
//Encode the properties of the object
[encoder encodeObject:self.contact_fname forKey:#"contact_fname"];
[encoder encodeObject:self.contact_lname forKey:#"contact_lname"];
[encoder encodeObject:self.contact_image forKey:#"contact_image"];
[encoder encodeObject:self.contact_phone_number forKey:#"contact_phone_number"];
}
-(id)initWithCoder:(NSCoder *)decoder
{
self = [super init];
if ( self != nil )
{
//decode the properties
self.contact_fname = [decoder decodeObjectForKey:#"contact_fname"];
self.contact_lname = [decoder decodeObjectForKey:#"contact_lname"];
self.contact_image = [decoder decodeObjectForKey:#"contact_image"];
self.contact_phone_number = [decoder decodeObjectForKey:#"contact_phone_number"];
}
return self;
}
then
-(void)writeArrayWithCustomObjToUserDefaults:(NSString *)keyName withArray:(NSMutableArray *)myArray
{
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:myArray];
[defaults setObject:data forKey:keyName];
[defaults synchronize];
}
-(NSArray *)readArrayWithCustomObjFromUserDefaults:(NSString*)keyName
{
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSData *data = [defaults objectForKey:keyName];
NSArray *myArray = [NSKeyedUnarchiver unarchiveObjectWithData:data];
[defaults synchronize];
return myArray;
}
use these functions for storing and reading custom object arrays
or u could simply use this library https://github.com/roomorama/RMMapper
There is a constraint on what kind of objects can be stored in the user defaults, detailed on the Apple documentation page:
The value parameter can be only property list objects: NSData, NSString, NSNumber, NSDate, NSArray, or NSDictionary. For NSArray and NSDictionary objects, their contents must be property list objects. See What is a Property List? in Property List Programming Guide.
You will need to convert your array of objects to an array of dictionaries, one of the ways to achieve this is to implement a toDictionary method that will take all properties and put them into a dictionary.
You'll also need the reverse method, an initWithDictionary: if you'll also want to re-create the objects from the user defaults (you can use the converted dictionaries if you want, though).
Once you have the conversion methods, you can use the NSUserDefaults methods to store and retrieve the objects.
This question already has answers here:
Write custom object to .plist in Cocoa
(3 answers)
Closed 9 years ago.
I have a custom class called ServerModule which is a subclass of NSObject. I'm basically storing all of these ServerModules with a key-value pair in an NSMutableDictionary. The dictionary is then stored in NSUserDefaults. I learned that NSUserDefaults only returns an immutable version of the object when it is accessed, so I changed my dictionary initialization to this:
_AllModules = [[NSMutableDictionary alloc]initWithDictionary:[_editServerModules objectForKey:#"AllModules"]]; //initialize a copy of AllModules dictionary
Now, I am simply trying to store a custom ServerModule object in this dictionary, and sync it. The following code attempts to do this:
//Create new ServerModule
ServerModule* newServer = [[ServerModule alloc]initWithUUID];
newServer.name = self.tf_name.text;
newServer.ip = self.tf_ip.text;
newServer.port = self.tf_port.text;
newServer.username = self.tf_user.text;
newServer.password = self.tf_pass.text;
//Add the ServerModule to AllModules dictionary with the key of its identifier
[_AllModules setObject:newServer forKey:newServer.identifier];
[self updateData];
[_editServerModules synchronize];
The identifier is a string which is set in the constructor of ServerModule. Here is the code for updateData.
[_editServerModules setObject:_AllModules forKey:#"AllModules"];
In case you are wondering, the object at #"AllModules" is initialized in the AppDelegate as follows:
NSMutableDictionary* AllModules = [[NSMutableDictionary alloc]init];
Once again, here is the error I am getting when I try to save something:
Attempt to set a non-property-list object {
"42E9EEA0-9051-4E2A-81EA-DC8FC5639C26" = "<ServerModule: 0x8ac4e50>";
} as an NSUserDefaults value for key AllModules
Thanks for any help!
~Carpetfizz
You can only store property list types (array, data, string, number, date, dictionary) or urls in NSUserDefaults. This means that everything, including any nested dictionary values, must be property list types. You'll want to implement the NSCoding protocol on your ServerModule object and then use NSKeyedArchiver to serialize your data before storing it and and NSKeyedUnarchiver to deflate your data after reading it back out of NSUserDefaults.
For example, given the properties you've shown exist on ServerModule objects, I'd add the following NSCoding protocol methods to your ServerModule implementation:
#pragma mark - NSCoding support
-(void)encodeWithCoder:(NSCoder*)encoder {
[encoder encodeObject:self.name forKey:#"name"];
[encoder encodeObject:self.ip forKey:#"ip"];
[encoder encodeObject:self.port forKey:#"port"];
[encoder encodeObject:self.username forKey:#"username"];
[encoder encodeObject:self.password forKey:#"password"];
}
-(id)initWithCoder:(NSCoder*)decoder {
self.name = [decoder decodeObjectForKey:#"name"];
self.ip = [decoder decodeObjectForKey:#"ip"];
self.port = [decoder decodeObjectForKey:#"port"];
self.username = [decoder decodeObjectForKey:#"username"];
self.password = [decoder decodeObjectForKey:#"password"];
return self;
}
And then of course you'll need to serialize:
NSData* archivedServerModules = [NSKeyedArchiver archivedDataWithRootObject:_AllModules];
[_editServerModules setObject:archivedServerModules forKey:#"AllModules"];
and deflate appropriately:
NSData* archivedServerModules = [_editServerModules objectForKey:#"AllModules"];
NSDictionary* serverModules = [NSKeyedUnarchiver unarchiveObjectWithData:archivedServerModules];
Hopefully that gives you an idea of what I'm talking about.
I have an array that is broken down like this:
PersonArray
PersonObject
1. NSstring (personsName)
2. NSMutableArray (EventsObject)
I am able to save this array with the code below:
NSMutableArray *archiveArray = [NSMutableArray arrayWithCapacity:mutableDataArray.count];
for (BC_Person *personObject in mutableDataArray)
{
NSData *personEncodedObject = [NSKeyedArchiver archivedDataWithRootObject:personObject];
[archiveArray addObject:personEncodedObject];
}
NSUserDefaults *userData = [NSUserDefaults standardUserDefaults];
[userData setObject:archiveArray forKey:#"personDataArray"];
[userData synchronize];
What I am stuck on is: If I add an object to the NSMutableArray inside of the Person object, do I also have to turn that array of object (EventsObject) into NSData?
(I would assume I do, but I can't seem to figure out how to target that array of objects (EventsObject) to convert inside the PersonArray.
I hope I am explaining this in a manner that is easy to understand.
NSArray and some other classes implement the NSCoding protocol. So you can simplify your code to:
NSData *personData = [NSKeyedArchiver archivedDataWithRootObject:mutableDataArray];
NSUserDefaults *userData = [NSUserDefaults standardUserDefaults];
[userData setObject:personData forKey:#"personDataArray"];
[userData synchronize];
And for any objects to be archived you implement the NSCoding protocol. So for BC_Person it could look like this (I do not know the class so I am making up some properties as an example):
- (void)encodeWithCoder:(NSCoder *)encoder {
[encoder encodeObject:self.firstName forKey:#"firstName"];
[encoder encodeObject:self.lastName forKey:#"lastName"];
[encoder encodeObject:self.eventObjects forKey:#"eventObjects"];
}
In this example self.eventObjects would be an array of objects of your EventsObject class. The array knows how to encode itself, and for EventsObject you implement another -encodeWithCoder: method.
And to unarchive you reverse the process.
I have an NSMutableArray in which I store objects called "address". An address is an NSObject with 4-5 properties (NSStrings). The NSMutableArray shall contain a maximum of 15 address object.
What is the best way to store that array on the iPhone? Core data? NSUserDefaults? Should I maybe store every address object by itself, and not all objects in one NSMutableArray? In that case what should I do on the iPhone?
as #rog said, you may use NSUserDefaults to save data
& you should make your object follow protocal NSCoding
for examplem if you object is "YouObject"
#interface YouObject: NSObject {
}
#property (nonatomic, copy) NSString *uid;
#property (nonatomic, copy) NSString *name;
#end
//implement this 2 method
- (id)initWithCoder:(NSCoder *)decoder {
if (self = [super init]) {
self.title = [decoder decodeObjectForKey:#"uid"];
self.author = [decoder decodeObjectForKey:#"name"];
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)encoder {
[encoder encodeObject:title forKey:#"uid"];
[encoder encodeObject:author forKey:#"name"];
}
then archive or unarchive using NSUserDefaults
//archive
YouObject *object = [YouObject ....]
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:object ];
[[NSUserDefaults standardUserDefaults] setObject:data forKey:#"address"];
//unarchive
NSData *data = [[NSUserDefaults standardUserDefaults] objectForKey:#"address"];
YouObject *object = (YouObject *)[NSKeyedUnarchiver unarchiveObjectWithData:data];
or if you have a YouObject Array, you can save the NSArray in the same way;
//archive
NSArray *addresses;
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:address ];
[[NSUserDefaults standardUserDefaults] setObject:data forKey:#"address"];
//unarchive
NSData *addressData = [[NSUserDefaults standardUserDefaults] objectForKey:#"address"];
NSArray *addresses = (NSArray*)[NSKeyedUnarchiver unarchiveObjectWithData:address];
For what you're describing, I think NSUserDefaults will suffice. See this post: How to store custom objects in NSUserDefaults. You can learn more about the limitations of NSUserDefaults here: What are the limitations of NSUserDefaults.
However, if you're saving/loading a large amount of data, then you should consider using Core Data.
Simplest way would to use the nsmutablearray read and write methods. All the data has to be plist data type nsarray nsdictionary nsstring nsnumber nsdate. Nsuserdefault like rog suggested is also good. As long as the amount of data remains small.
I'd like to save an NSMutableDictionary object in NSUserDefaults. The key type in NSMutableDictionary is NSString, the value type is NSArray, which contains a list of object which implements NSCoding. Per document, NSString and NSArray both are conform to NSCoding.
I am getting this error:
[NSUserDefaults setObject: forKey:]: Attempt to insert non-property value.... of class NSCFDictionary.
I found out one alternative, before save, I encode the root object (NSArray object) using NSKeyedArchiver, which ends with NSData. Then use UserDefaults save the NSData.
When I need the data, I read out the NSData, and use NSKeyedUnarchiver to convert NSData back to the object.
It is a little cumbersome, because i need to convert to/from NSData everytime, but it just works.
Here is one example per request:
Save:
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSMutableArray *arr = ... ; // set value
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:arr];
[defaults setObject:data forKey:#"theKey"];
[defaults synchronize];
Load:
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSData *data = [defaults objectForKey:#"theKey"];
NSArray *arr = [NSKeyedUnarchiver unarchiveObjectWithData:data];
The element in the array implements
#interface CommentItem : NSObject<NSCoding> {
NSString *value;
}
Then in the implementation of CommentItem, provides two methods:
-(void)encodeWithCoder:(NSCoder *)encoder
{
[encoder encodeObject:value forKey:#"Value"];
}
-(id)initWithCoder:(NSCoder *)decoder
{
self.value = [decoder decodeObjectForKey:#"Value"];
return self;
}
Anyone has better solution?
Thanks everyone.
If you're saving an object in user defaults, all objects, recursively, all the way down, must be property list objects. Conforming to NSCoding doesn't mean anything here-- NSUserDefaults won't automatically encode them into NSData, you have to do that yourself. If your "list of object which implements NSCoding" means objects that are not property list objects, then you'll have to do something with them before saving to user defaults.
FYI the property list classes are NSDictionary, NSArray, NSString, NSDate, NSData, and NSNumber. You can write mutable subclasses (like NSMutableDictionary) to user preferences but the objects you read out will always be immutable.
Are all of your keys in the dictionary NSStrings? I think they have to be in order to save the dictionary to a property list.
Simplest Answer :
NSDictionary is only a plist object , if the keys are NSStrings.
So, Store the "Key" as NSString with stringWithFormat.
Solution :
NSString *key = [NSString stringWithFormat:#"%#",[dictionary valueForKey:#"Key"]];
Benefits :
It will add String-Value.
It will add Empty-Value when your Value of Variable is NULL.
Have you considered looking at implementing the NSCoding Protocol? This will allow you encode and decode on the iPhone with two simple methods that are implemented with the NSCoding. First you would need to adding the NSCoding to your Class.
Here is an example:
This is in the .h file
#interface GameContent : NSObject <NSCoding>
Then you will need to implement two methods of the NSCoding Protocol.
- (id) initWithCoder: (NSCoder *)coder
{
if (self = [super init])
{
[self setFoundHotSpots:[coder decodeObjectForKey:#"foundHotSpots"]];
}
return self;
}
- (void) encodeWithCoder: (NSCoder *)coder
{
[coder encodeObject:foundHotSpots forKey:#"foundHotSpots"];
}
Check out the documentation on NSCoder for more information. That has come in really handy for my projects where I need to save the state of the application on the iPhone if the application is closed and restore it back to it's state when its back on.
The key is to add the protocol to the interface and then implement the two methods that are part of NSCoding.
I hope this helps!
There is no better solution. Another option would be to just save the coded object to disk - but that is doing the same thing. They both end up with NSData that gets decoded when you want it back.