Converting a NSObject into NSData - iphone

I am having an issue in converting a NSObject into NSData. I have a class which inherits NSObject.
When i tried to convert the object of that particular class into NSData as follows :
NSData *dataOnObject = [NSKeyedArchiver archivedDataWithRootObject:classObject];
but it gives out exception stating that -[classObject encodeWithCoder:]: unrecognized selector sent to instance ..
I have also added the object to a newly created array as
NSMutableArray *wrapperedData = [NSMutableArray arrayWithObject: classObject];
NSData *dataOnObject = [NSKeyedArchiver archivedDataWithRootObject:value];
But still , its giving out exception.
So I need to extract the bytes from the object classObject.
Any help would be greatly appreciated ...
awaiting for your reply ...

You must implement for your own object such as:
- (void)encodeWithCoder:(NSCoder *)aCoder
{
[aCoder encodeObject:self.name forKey:#"name"];
[aCoder encodeInt:self.age forKey:#"age"];
[aCoder encodeObject:self.email forKey:#"email"];
[aCoder encodeObject:self.password forKey:#"password"];
}
BOOL success = [NSKeyedArchiver archiveRootObject:person toFile:archiveFilePath];
and:
- (id)initWithCoder:(NSCoder *)aDecoder
{
if (self = [super init]) {
self.name = [aDecoder decodeObjectForKey:#"name"];
self.age = [aDecoder decodeIntForKey:#"age"];
self.email = [aDecoder decodeObjectForKey:#"email"];
self.password = [aDecoder decodeObjectForKey:#"password"];
}
return self;
}
Person *unarchivePerson = [NSKeyedUnarchiver unarchiveObjectWithFile:archiveFilePath];

You need to implement encodeWithCoder: on your custom class, serializing all of its attributes using the NSCoder passed into it. If its attributes include any more custom classes, they'll need encodeWithCoder: implementing too.

Instead of
NSData *dataOnObject = [NSKeyedArchiver archivedDataWithRootObject:classObject];
it should be
NSData *dataOnObject = [[NSUserDefaults standardUserDefaults] objectForKey:#"someKey"];
But that's just for reading data in that's already been saved. If you want to save an object as NSData then you have this:
[[NSUserDefaults standardUserDefaults] setObject:[NSKeyedArchiver archivedDataWithRootObject:classObject] forKey:#"someKey"];
But that's not all. Your classObject has to implement the NSCoding protocol and have the two methods encodeWithCoder: and initWithCoder: since it's not an NS object in order for it to work.

you can only archive objects that support the NSCoding protocol

You can convert any object to NSData with the NSCoding protocol.
You can find sample code to do this here:
http://samsoff.es/posts/archiving-objective-c-objects-with-nscoding

This is a example of custom object converted to NSData (so it can be then saved into user defaults)
Create the following files:
Catalog.h
#interface Catalog : NSObject
#property (nonatomic, assign) int pk;
#property (nonatomic, copy) NSString *catalogName;
#property (nonatomic, copy) NSString *catalogDescription;
#property (nonatomic, assign) int catalogEdition;
#property (nonatomic, assign) int catalogTotalPages;
- (void)encodeWithCoder:(NSCoder *)aCoder;
- (id)initWithCoder:(NSCoder *)aDecoder;
#end
Catalog.m
#import <Foundation/Foundation.h>
#import "Catalog.h"
#implementation Catalog
- (void)encodeWithCoder:(NSCoder *)aCoder {
[aCoder encodeObject:self.catalogName forKey:#"catalogName"];
[aCoder encodeObject:self.catalogDescription forKey:#"catalogDescription"];
[aCoder encodeInt:self.catalogEdition forKey:#"catalogEdition"];
[aCoder encodeInt:self.catalogTotalPages forKey:#"catalogTotalPages"];
}
- (id)initWithCoder:(NSCoder *)aDecoder
{
if (self = [super init]) {
self.catalogName = [aDecoder decodeObjectForKey:#"catalogName"];
self.catalogDescription = [aDecoder decodeObjectForKey:#"catalogDescription"];
self.catalogEdition = [aDecoder decodeIntForKey:#"catalogEdition"];
self.catalogTotalPages = [aDecoder decodeIntForKey:#"catalogTotalPages"];
}
return self;
}
#end
Finally in your controller include header files
#import "Catalog.h"
And add this code to use your object (in this case im saving into user defaults)
Catalog *catalog = [[Catalog alloc] init];
catalog.catalogName = #"catalogName";
catalog.catalogDescription = #"catalogName";
catalog.catalogEdition = 1;
NOTE: in this line of code is where the actual data passing is taking place
//archiving object to nsdata
NSData *encodedObject = [NSKeyedArchiver archivedDataWithRootObject:catalog];
[[NSUserDefaults standardUserDefaults] setObject:encodedObject forKey:#"keyName"];
[[NSUserDefaults standardUserDefaults] synchronize];
In case you want to get your object back from NSData
NSData *nsData = [[NSUserDefaults standardUserDefaults] objectForKey:#"keyName"];
//unarchiving object to nsdata
Catalog *selectedCatalog = [NSKeyedUnarchiver unarchiveObjectWithData: nsData];
Hope this helps!

Related

iOS - Saving xml data after parsing

This question can be a duplicated one. But I was unable to find a proper answer for my issue.
I have following xml file downloading from a server.
<INCIDENTTYPES>
<INCIDENT FORMNAME="first" TEXT="First incident">
<TYPE>Type1</TYPE>
<TYPE>Type2</TYPE>
<TYPE>Type3 Tag</TYPE>
<TYPE>Type4 Tag</TYPE>
<LOCATION>Council</LOCATION>
<LOCATION>Domestic</LOCATION>
<LOCATION>Commercial</LOCATION>
</INCIDENT>
<INCIDENT FORMNAME="second" TEXT="Second incident">
<TYPE>Second type</TYPE>
<TYPE>Second/first type</TYPE>
<TYPE>Second3</TYPE>
<LOCATION>Council</LOCATION>
<LOCATION>Domestic</LOCATION>
<LOCATION>Commercial</LOCATION>
</INCIDENT>
</INCIDENTTYPES>
I am using RaptureXML to parse the xml file and parsing can be done as follows.
RXMLElement *rootXML = [RXMLElement elementFromXMLData:self.connectionData];
[rootXML iterate:#"INCIDENT" usingBlock: ^(RXMLElement *incidents) {
if([[incidents attribute:#"FORMNAME"] isEqualToString:#"first"]){
NSArray *listArray = [NSArray array];
listArray = [incidents children:#"TYPE"];
IncidentData *iData = [[IncidentData alloc] init];
[iData.type setValue:listArray forKey:#"type"];
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:iData];
[self.userDefault setObject:data forKey:#"firstObject"];
}
}
The IncidentData class as follows.
#import "IncidentData.h"
#implementation IncidentData
- (id)initWithCoder:(NSCoder *)aDecoder{
if (self = [super init]) {
//self.type is an array
self.type = [aDecoder decodeObjectForKey:#"type"];
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)aCoder{
[aCoder encodeObject:self.type forKey:#"type"];
}
#end
I am trying to load data as following.
NSData *dd = [self.userDefault objectForKey:#"firstObject"];
IncidentData *items = (IncidentData *)[NSKeyedUnarchiver unarchiveObjectWithData:dd];
NSArray *typeArray = [items.type objectAtIndex:0];
NSLog(#"Incident type : %#", typeArray);
But typeArray is null.
Summary: I am trying to parse a remote xml file and save the values on to the disk and use later stage of the app.
Can you please help me on this ?
RaptureXML, RXMLElement might not support NSCoding.
try substituting:
listArray = [incidents children:#"TYPE"];
with:
listArray = [[incidents children:#"TYPE"] valueForKeyPath:#"text"];

how to decode multiple objects from data stored in file in iOS

I have a form with information first name and last name and some other information. I use a person class to store this information. on submit click I archiving it in a file person.txt using NSCoding implemented in person class. if I add multiple persons in the file person.txt, how can I get all the person objects stored in the file. decoding the person class just gives me the last added person.
If you want all of the person objects serialized, then you need the NSArray or whatever other collection class in which they are stored to be the root object for the NSKeyedArchiver. e.g.: (assumes ARC)
#import <Foundation/Foundation.h>
#interface Person:NSObject <NSCoding>
#property (nonatomic, copy) NSString *lastName;
#property (nonatomic, copy) NSString *firstName;
// etc.
#end
#implementation Person
#synthesize lastName = _lastName;
#synthesize firstName = _firstName;
- (void)encodeWithCoder:(NSCoder *)aCoder {
[aCoder encodeObject:self.lastName forKey:#"ln"];
[aCoder encodeObject:self.firstName forKey:#"fn"];
}
- (id)initWithCoder:(NSCoder *)aDecoder {
self = [super init];
if( !self ) { return nil; }
_lastName = [aDecoder decodeObjectForKey:#"ln"];
_firstName = [aDecoder decodeObjectForKey:#"fn"];
return self;
}
#end
int main(int argc, char *argv[]) {
NSAutoreleasePool *p = [[NSAutoreleasePool alloc] init];
Person *me = [Person new];
me.lastName = #"Kitten";
me.firstName = #"Mittens";
Person *you = [Person new];
you.lastName = #"Youe";
you.firstName = #"JoJo";
NSArray *people = [NSArray arrayWithObjects:me,you,nil];
NSData *serializedData = [NSKeyedArchiver archivedDataWithRootObject:people];
// write your serializedData to file, etc.
[p release];
}
Why the .txt extension on your archive, though? It just binary data, right?

initWithCoder for loading xib example [duplicate]

This question already has answers here:
Objective C - How do I use initWithCoder method?
(2 answers)
Closed 9 years ago.
I was reading about initializing the archived objects from a XIB file and found that
- (id)initWithCoder:(NSCoder *)aDecoder
is a way of doing it. But I am not able to get a hang around this. Can someone show me an simple example of how to do this?
Thanks a ton
The NSCoder class is used to archive/unarchive (marshal/unmarshal, serialize/deserialize) of objects.
This is a method to write objects on streams (like files, sockets) and being able to retrieve them later or in a different place.
I would suggest you to read Archiving
You also need to define the following method as follows:
- (void)encodeWithCoder:(NSCoder *)enCoder
{
[super encodeWithCoder:enCoder];
[enCoder encodeObject:instanceVariable forKey:INSTANCEVARIABLE_KEY];
// Similarly for the other instance variables.
....
}
And in the initWithCoder method initialize as follows:
- (id)initWithCoder:(NSCoder *)aDecoder
{
if(self = [super initWithCoder:aDecoder]) {
self.instanceVariable = [aDecoder decodeObjectForKey:INSTANCEVARIABLE_KEY];
// similarly for other instance variables
....
}
return self;
}
You can initialize the object standard way i.e
CustomObject *customObject = [[CustomObject alloc] init];
Example taken from this answer
You can use it in following way:
.h file
#interface Score : NSObject {
NSString *Username;
NSString *TotalPoints;
NSString *LifeRemains;
NSString *ScoreDate;
}
#property (nonatomic, retain) NSString *Username;
#property (nonatomic, retain) NSString *TotalPoints;
#property (nonatomic, retain) NSString *LifeRemains;
#property (nonatomic, retain) NSString *ScoreDate;
in .m file
#synthesize Username, TotalPoints, LifeRemains, ScoreDate;
- (void)encodeWithCoder:(NSCoder *)encoder
{
//Encode properties, other class variables, etc
[encoder encodeObject:self.Username forKey:kScoreUsername];
[encoder encodeObject:self.TotalPoints forKey:kScoreTotalPoints];
[encoder encodeObject:self.LifeRemains forKey:kScoreLifeRemains];
[encoder encodeObject:self.ScoreDate forKey:kScoreDate];
}
- (id)initWithCoder:(NSCoder *)decoder
{
self = [super init];
if( self != nil )
{
//decode properties, other class vars
self.Username = [decoder decodeObjectForKey:kScoreUsername];
self.TotalPoints = [decoder decodeObjectForKey:kScoreTotalPoints];
self.LifeRemains = [decoder decodeObjectForKey:kScoreLifeRemains];
self.ScoreDate = [decoder decodeObjectForKey:kScoreDate];
}
return self;
}
Happy Coding...

Overwrite a value saved in NSUserDefaults

I have this method to save a mutable array named myWallet that contains instances of the Class Card.
- (void)saveMyWallet
{
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:[NSKeyedArchiver archivedDataWithRootObject:self.myWallet] forKey:#"myWalletArray"];
[defaults synchronize];
}
The Card Class that I have has three instance variables: name, pin, and points. So far, saving new instances of the Card in UserDefaults is ok. I would just like to know some suggestions on how can I overwrite the value of points because as I proceed in the computation of points, I want to update it.
Here is my Card Class
Card.h
#import <Foundation/Foundation.h>
#interface Card : NSObject <NSCoding>
#property (nonatomic, strong) NSString *name;
#property (nonatomic, strong) NSString *pin;
#property (nonatomic, strong) NSNumber *points;
#property (nonatomic, strong) NSMutableArray *pointsToDeduct;
- (double) subtractPoints: (double) requiredPoints;
- (void) encodeWithCoder:(NSCoder *)coder;
- (id) initWithCoder: (NSCoder *)coder;
#end
Card.m
#import "Card.h"
#implementation Card
#synthesize name = _name;
#synthesize pin = _pin;
#synthesize points = _points;
#synthesize pointsToDeduct = _pointsToDeduct;
- (id)initWithCoder:(NSCoder *)coder
{
self = [[Card alloc] init];
if(self != nil) {
self.name = [coder decodeObjectForKey:#"name"];
self.pin = [coder decodeObjectForKey:#"pin"];
self.points = [coder decodeObjectForKey:#"points"];
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)coder
{
[coder encodeObject:self.name forKey:#"name"];
[coder encodeObject:self.pin forKey:#"pin"];
[coder encodeObject:self.points forKey:#"points"];
}
- (double) subtractPoints:(double) requiredPoints
{
double latestPoints;
latestPoints = ([self.points doubleValue] - requiredPoints);
return latestPoints;
}
#end
And lastly, here is the delegate method by which the new value of the points (named resultingPoints) should come from.
- (void)perksDetailsViewController:(PerksDetailsViewController *)sender didPassRequiredPoints:(NSNumber *)requiredPoints withCard:(Card *)selectedCard
{
double perksPoints = [requiredPoints doubleValue];
self.resultingPoints = [NSNumber numberWithDouble:[selectedCard subtractPoints:perksPoints] ];
NSLog(#"points remaining %#", self.resultingPoints);
}
Bombard me with suggestions :) Thanks in advance!
From what I see, you actually save your object as NSData, so the logical approach is to get it back from the user defaults, unarchive it, update the properties, archive it and save it back to the user defaults.
Retrive the data from NSUserDefaults into runtime,Delete previous object for key and write back updated value.

How to serialize a class in IOS sdk (Objective-c)?

How to serialize the following class in objective-c so that it can be used with SBJson?
I get "JSON serialisation not supported for Animal" error when I use this code.
Can someone point out where I am going wrong?
The contents of Animal.h file is as below
#import <UIKit/UIKit.h>
#interface Animal : NSObject<NSCoding> {
NSString *name;
NSString *description;
NSString *imageURL;
}
#property (nonatomic, retain) NSString *name;
#property (nonatomic, retain) NSString *description;
#property (nonatomic, retain) NSString *imageURL;
-(id)initWithName:(NSString *)n description:(NSString *)d url:(NSString *)u;
#end
The contents of Animal.m file is as below
#import "Animal.h"
#implementation Animal
#synthesize name, description, imageURL;
-(id)initWithName:(NSString *)n description:(NSString *)d url:(NSString *)u {
self.name = n;
self.description = d;
self.imageURL = u;
return self;
}
- (id)initWithCoder:(NSCoder *)aDecoder
{
if(self = [super initWithCoder:aDecoder])
{
name = [[aDecoder decodeObjectForKey:#"name"] retain];
description = [[aDecoder decodeObjectForKey:#"description"] retain];
imageURL = [[aDecoder decodeObjectForKey:#"imageURL"] retain];
}
return [self initWithName:name description:description url:imageURL];
}
- (void)encodeWithCoder:(NSCoder *)encoder
{
[super encodeWithCoder:encoder];
[encoder encodeObject:name forKey:#"name"];
[encoder encodeObject:description forKey:#"description"];
[encoder encodeObject:imageURL forKey:#"imageURL"];
}
#end
Make your custom class conform to NSCoding protocol and then serialize it.
For more info, visit the Apple documentation
Also, this link will also help you.
As suggested in this link, archive your custom class to NSData and serialize that as provided in the Apple documentation.
Edit
Make your Animal.m as follows:
#import "Animal.h"
#implementation Animal
#synthesize name, description, imageURL;
-(id)initWithName:(NSString *)n description:(NSString *)d url:(NSString *)u {
self = [super init];
if( self )
{
self.name = n;
self.description = d;
self.imageURL = u;
}
return self;
}
- (id)initWithCoder:(NSCoder *)aDecoder
{
self = [super init];
if( self )
{
self.name = [aDecoder decodeObjectForKey:#"name"];
self.description = [aDecoder decodeObjectForKey:#"description"];
self.imageURL = [aDecoder decodeObjectForKey:#"imageURL"];
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)encoder
{
[encoder encodeObject:name forKey:#"name"];
[encoder encodeObject:description forKey:#"description"];
[encoder encodeObject:imageURL forKey:#"imageURL"];
}
#end
To actually answer your question on how to do it using SBJson: Implement the proxyForJson method. Unless you are serializing an NSArray or NSDictionary you must override this method for serialization to work.
You can see that this is the case by looking at the SBJson source code (in SBJsonWriter.m):
- (NSData*)dataWithObject:(id)object {
...
if ([object isKindOfClass:[NSDictionary class]])
ok = [streamWriter writeObject:object];
else if ([object isKindOfClass:[NSArray class]])
ok = [streamWriter writeArray:object];
else if ([object respondsToSelector:#selector(proxyForJson)])
return [self dataWithObject:[object proxyForJson]];
else {
self.error = #"Not valid type for JSON";
return nil;
}
...
}
}
Implement proxyForJson in Animal.m like this (not tested):
- (NSDictionary*) proxyForJson
{
return [NSDictionary dictionaryWithObjectsAndKeys:self.name, #"name",
self.description, #"description",
self.imageURL, #"imageURL",
nil];
}
This open source project JSONCoding makes the whole process pretty simple, using the new sdk class in conjunction with the NSCoding protocol.
Check the newly introduced NSJSONSerialization class:
NSJSONSerialization class
I think you can check out this if it helps you: Make a Custom Class Serializable
Please check this Property List Programming Guide - Serializing a Property List
and the similar post here:
Make a Custom Class Serializable in Objective-c/iPhone?
Object serialization in XML format using Obj-C / iPhone SDK
See also https://github.com/jsonmodel/jsonmodel
Magical Data Modeling Framework for JSON - allows rapid creation of
smart data models. You can use it in your iOS, macOS, watchOS and tvOS
apps.
This is also the chosen library for the Objective-c Swagger client.