Send and receive NSData via GameKit - iphone

I'm trying to send some NSData over Bluetooth through GameKit.
While I've got GameKit set up and are able to send small messages across, I now would like to expand and send across whole files.
I've been reading that you have to split large files up into packets before sending them across individually.
So I decided to create a struct to make it easier to decode the packets when they're received at the other end:
typedef struct {
const char *fileName;
NSData *contents;
int fileType;
int packetnumber;
int totalpackets;
} file_packet;
However, for small files (8KB and less) I thought one packet will be enough.
So for one packet, I thought I would be able to create a file_packet, set its properties, and send it via -sendDataToAllPeers:withDataMode:error:
NSData *fileData;
file_packet *packet = (file_packet *)malloc(sizeof(file_packet));
packet->fileName = [filename cStringUsingEncoding:NSASCIIStringEncoding];
packet->contents = [NSData dataWithContentsOfFile:selectedFilePath];
packet->packetnumber = 1;
packet->totalpackets = 1;
packet->fileType = 56; //txt document
fileData = [NSData dataWithBytes:(const void *)packet length:sizeof(file_packet)];
free(packet);
NSError *error = nil;
[self.connectionSession sendDataToAllPeers:fileData withDataMode:GKSendDataReliable error:&error];
if (error) {
NSLog(#"An error occurred: %#", [error localizedDescription]);
}
However, I don't think something's right setting fileData - and error displays nothing.
When a file's received, I do the following:
file_packet *recievedPacket = (file_packet *)malloc(sizeof(file_packet));
recievedPacket = (file_packet *)[data bytes];
NSLog(#"packetNumber = %d", recievedPacket->packetnumber);
...
However, the output on the console is packetNumber = 0, even when I set packetNumber to 1.
Am I missing the obvious?
I don't know much about NSData or GameKit.
So my question is - Can I add a file_packet in NSData, and if so, How do I do it successfully - and How do you split files up into multiple packets?

To add on:
What you ought to do here is make an NSObject subclass to represent your packet, and then adopt NSCoding to serialize it to an NSData in the way that you want. Doing this with a struct isn't buying you anything, and makes things even harder. It's also fragile, since packing a struct into an NSData doesn't account for things like endian-ness, etc.
The tricky part of the packetizing process using NSCoding is that you don't really know what the overhead of the coding process is, so being as big as possible, but still under the max packet size is tricky...
I present this without testing, or warranty, but if you want a decent start on that approach, this may be it. Be warned, I didn't check to see if my arbitrary 100 bytes for overhead was realistic. You'll have to play with the numbers a little bit.
Packet.h:
#interface Packet : NSObject <NSCoding>
{
NSString* fileName;
NSInteger fileType;
NSUInteger totalPackets;
NSUInteger packetIndex;
NSData* packetContents;
}
#property (readonly, copy) NSString* fileName;
#property (readonly, assign) NSInteger fileType;
#property (readonly, assign) NSUInteger totalPackets;
#property (readonly, assign) NSUInteger packetIndex;
#property (readonly, retain) NSData* packetContents;
+ (NSArray*)packetsForFile: (NSString*)name ofType: (NSInteger)type withData: (NSData*)fileContents;
#end
Packet.m:
#import "Packet.h"
#interface Packet ()
#property (readwrite, assign) NSUInteger totalPackets;
#property (readwrite, retain) NSData* packetContents;
#end
#implementation Packet
- (id)initWithFileName: (NSString*)pFileName ofType: (NSInteger)pFileType index: (NSUInteger)pPacketIndex
{
if (self = [super init])
{
fileName = [pFileName copy];
fileType = pFileType;
packetIndex = pPacketIndex;
totalPackets = NSUIntegerMax;
packetContents = [[NSData alloc] init];
}
return self;
}
- (void)dealloc
{
[fileName release];
[packetContents release];
[super dealloc];
}
#synthesize fileName;
#synthesize fileType;
#synthesize totalPackets;
#synthesize packetIndex;
#synthesize packetContents;
- (void)encodeWithCoder:(NSCoder *)aCoder
{
[aCoder encodeObject: self.fileName forKey: #"fileName"];
[aCoder encodeInt64: self.fileType forKey:#"fileType"];
[aCoder encodeInt64: self.totalPackets forKey:#"totalPackets"];
[aCoder encodeInt64: self.packetIndex forKey:#"packetIndex"];
[aCoder encodeObject: self.packetContents forKey:#"totalPackets"];
}
- (id)initWithCoder:(NSCoder *)aDecoder
{
if (self = [super init])
{
fileName = [[aDecoder decodeObjectForKey: #"fileName"] copy];
fileType = [aDecoder decodeInt64ForKey:#"fileType"];
totalPackets = [aDecoder decodeInt64ForKey:#"totalPackets"];
packetIndex = [aDecoder decodeInt64ForKey:#"packetIndex"];
packetContents = [[aDecoder decodeObjectForKey:#"totalPackets"] retain];
}
return self;
}
+ (NSArray*)packetsForFile: (NSString*)name ofType: (NSInteger)type withData: (NSData*)fileContents
{
const NSUInteger quanta = 8192;
Packet* first = [[[Packet alloc] initWithFileName:name ofType:type index: 0] autorelease];
// Find out how big the NON-packet payload is...
NSMutableData* data = [NSMutableData data];
NSKeyedArchiver* coder = [[[NSKeyedArchiver alloc] initForWritingWithMutableData:data] autorelease];
[first encodeWithCoder: coder];
[coder finishEncoding];
const NSUInteger nonPayloadSize = [data length];
NSMutableArray* packets = [NSMutableArray array];
NSUInteger bytesArchived = 0;
while (bytesArchived < [fileContents length])
{
Packet* nextPacket = [[[Packet alloc] initWithFileName: name ofType: type index: packets.count] autorelease];
NSRange subRange = NSMakeRange(bytesArchived, MIN(quanta - nonPayloadSize - 100, fileContents.length - bytesArchived));
NSData* payload = [fileContents subdataWithRange: subRange];
nextPacket.packetContents = payload;
bytesArchived += [payload length];
[packets addObject: nextPacket];
}
for (Packet* packet in packets)
{
packet.totalPackets = packets.count;
}
return packets;
}
- (NSData*)dataForSending
{
NSMutableData* data = [NSMutableData data];
NSKeyedArchiver* coder = [[[NSKeyedArchiver alloc] initForWritingWithMutableData:data] autorelease];
[self encodeWithCoder: coder];
[coder finishEncoding];
return [NSData dataWithData:data];
}
+ (Packet*)packetObjectFromRxdData:(NSData*)data
{
NSKeyedUnarchiver* decoder = [[[NSKeyedUnarchiver alloc] initForReadingWithData:data] autorelease];
return [[[Packet alloc] initWithCoder:decoder] autorelease];
}
#end
The reassemblage of the original file from these packets can be done using much the same approach as splitting it up... Iterate over the packets, copying from the individual packet payload NSDatas into a big NSMutableData.
In closing, I feel compelled to say that when you find yourself doing something like this, that boils down to implementing a primitive TCP stack, it's usually time to stop yourself and ask if there aren't better ways to do this. Put differently, if GameKit were the best way to transfer files between devices over bluetooth, one would expect that the API would have a method for doing just that, but instead it has this 8K limit.
I'm not being intentionally cryptic -- I don't know what the right API would be for your situation, but the exercise of cooking up this Packet class left me thinking, "there's gotta be a better way."
Hope this helps.

You create the NSData with size sizeof(packet), which is only the pointer's size. Change it to sizeof(file_packet).
BTW, you're not really sending the filename and the contents. Only the pointers to them.

Related

writing data using NSKeyedArchiver

I trying to learn how to save array of objects using NSKeyedArchiver and I coded a small application to do that and I logged to see if the array was saved but everytime I get 0 for array count and here is the code.
ViewController.h
#interface ViewController : UIViewController
{
IBOutlet UITextField *text;
IBOutlet UITextField *textName;
IBOutlet UITextField *textAge;
IBOutlet UILabel *name;
IBOutlet UILabel *age;
BOOL flag;
BOOL choice;
NSString *documentDirectory;
NSMutableArray *anArray;
Person *p;
NSData *data;
}
-(BOOL) dataFilePath;
-(IBAction)readPlist;
-(IBAction) writePlist;
#property (strong,nonatomic)IBOutlet UITextField *text;
#property (strong,nonatomic)IBOutlet UITextField *textName;
#property (strong,nonatomic)IBOutlet UITextField *textAge;
#property (strong,nonatomic)IBOutlet UILabel *name;
#property (strong,nonatomic)IBOutlet UILabel *age;
#property (strong,nonatomic)NSString *documentDirectory;
#property (strong,nonatomic)NSMutableArray *anArray;
#end
ViewController.m
#interface ViewController ()
#end
#implementation ViewController
#synthesize text,documentDirectory,textAge,textName,name,age,anArray;
- (void)viewDidLoad
{
[super viewDidLoad];
// checking if the file was created and show a message if its created or not.
if ([self dataFilePath]) {
NSLog(#"File Created !");
} else {
NSLog(#"File Not Created !");
}
NSLog(#"File location : %#",documentDirectory);
choice = YES;
// Do any additional setup after loading the view, typically from a nib.
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
-(BOOL) dataFilePath
{
NSArray *path = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
documentDirectory = [path objectAtIndex:0];
documentDirectory = [documentDirectory stringByAppendingPathComponent:#"Milmers­Data.dat"];
return TRUE;
}
- (IBAction)writePlist
{
p.name = textName.text;
p.age = [textAge.text intValue];
[anArray addObject:p];
for (int i=0; i<[anArray count]+1; i++) {
Person *pp = [[Person alloc]init];
pp=[anArray objectAtIndex:i];
NSLog(#"Name: %#",pp.name); // checking the names in pp object but getting null
}
data = [NSKeyedArchiver archivedDataWithRootObject:anArray];
[data writeToFile:documentDirectory options:NSDataWritingAtomic error:nil];
NSLog(#"Array length: %d",[anArray count]); //Always got array count zero.
}
-(IBAction)readPlist
{
NSString *filePath = documentDirectory;
NSMutableArray *array = [NSKeyedUnarchiver unarchiveObjectWithFile:filePath];
NSLog(#"The array is: %#",array); // it shows that there is nothing in the array
}
#end
I wrote the class for writing .plist files originally but I knew later that I cant store objects in .plist file so I tried so that with archive, thats why the method name have plist in it.
Thank you in advance
Looks like you aren't ever creating an instance of p to add to the array. Try:
Person *p = [[Person alloc] init];
p.name = textName.text;
p.age = [textAge.text intValue];
[anArray addObject:p];
your index limit was also wrong in this loop
for (int i=0; i<[anArray count]; i++) {
NSLog(#"Name: %#", [[anArray objectAtIndex:i] name]);
}
you should really have been seeing a couple of different crashes...
Try adding this in viewDidLoad
[[NSFileManager defaultManager] createFileAtPath:documentDirectory contents:nil error:nil];
It looks like you never do this, and using archives to write to files only works if the file already exists (make sure you only do this once, otherwise every time that view is loaded the file will be emptied of all the data in it). And when you do this
if ([self dataFilePath])
It's pointless, because no matter what it always returns yes, whether the file exists or not.
Does your Person class implement NSCoding?
Specifically you need to implement something like the following in Person.m:
- (id)initWithCoder:(NSCoder *)decoder {
self = [super init];
if (!self) {
return nil;
}
self.name = [decoder decodeObjectForKey:#"name"];
self.age = [decoder decodeObjectForKey:#"age"];
return self;
}
- (void)encodeWithCoder:(NSCoder *)encoder {
[encoder encodeObject:self.name forKey:#"name"];
[encoder encodeObject:self.age forKey:#"age"];
}

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?

Message Sent to Deallocated Instance

I'm using TouchXML to parse an element in iOS. I retrieve a response from a web service using an NSInvocationOperation, then parse and display the results. Everything works fine as the background thread displays results on the main thread using [self performSelectorOnMainThread:#selector(displayLoginresult:) withObject:res waitUntilDone:NO]; but then I get an error:
2011-07-18 11:58:06.108 billsApp[873:7107] *** -[CFString release]: message sent to deallocated instance 0x5d809b0
The code to parse the element is:
-(LoginResult *) tryLogin:(NSString *)userName withPassword:(NSString*)password{
NSURL *url = [UrlUtility TryLogin:userName passwordHash:password];
CXMLDocument *responseObj = [UrlUtility xmlDocWithUrl:url];
if(responseObj == [NSNull null])
return [NSNull null];
CXMLElement *eleUser = [responseObj nodeForXPath:#"//User" error:nil];
CXMLElement *eleResult = [responseObj nodeForXPath:#"//Result" error:nil];
LoginResultType resultType;
//NSLog(#"Result: ");
//NSLog(eleResult );
// NSLog([[eleResult stringValue] lowercaseString]);
if ([[[eleResult stringValue] lowercaseString ] isEqualToString: #"successful"]){
resultType = Successful;
} else {
resultType = InvalidUsernameOrPassword;
}
LoginResult *res = [[LoginResult alloc] init];
res.result = resultType;
for (CXMLElement *resultElement in [responseObj children] ) {
NSLog([NSString stringWithFormat:#"%# %#", [resultElement name], [resultElement stringValue]]);
}
//todo: fix enum parsing =[LoginResult loginResultTypeStringToEnum: [eleResult stringValue]];
if(eleUser != nil) {
CXMLElement *eleClientID = [eleUser nodeForXPath:#"ClientID" error:nil];
CXMLElement *eleCompanyName = [eleUser nodeForXPath:#"CompanyName" error:nil];
CXMLElement *eleCompanyContact = [eleUser nodeForXPath:#"CompanyContact" error:nil];
CXMLElement *eleIsAgent = [eleUser nodeForXPath:#"IsAgent" error:nil];
CXMLElement *eleParentID = [eleUser nodeForXPath:#"ParentID" error:nil];
NSInteger *clientId = [[eleClientID stringValue] integerValue];
NSString *companyName = [eleCompanyName stringValue];
NSString *companyContact = [eleCompanyContact stringValue];
bool isAgent = [Utils stringToBool:[eleIsAgent stringValue]];
NSInteger *parentId = [[eleParentID stringValue] integerValue];
User *user = [[User alloc] initWithData:clientId companyName:companyName companyContact:companyContact isAgent:isAgent parentId:parentId];
res.user = user;
// release elements
// [eleClientID release];
// [eleCompanyName release];
// [eleCompanyContact release];
// [eleIsAgent release];
// [eleParentID release];
//release raw values
// [companyName release];
// [companyContact release];
}
// [eleResult release];
// [eleUser release];
return res;
}
Part of me wants to say it's a bug with TouchXML, but I find that very unlikely. Is there any way to further track down the error?
EDIT: The definitions for the properties on the User class is:
#property (nonatomic, readwrite) NSInteger clientId;
#property (nonatomic, retain) NSString *companyName;
#property (nonatomic, retain) NSString *companyContact;
#property (nonatomic, readwrite) bool isAgent;
#property (nonatomic, readwrite) NSInteger parentId;
And the instance is initialized with:
-(User*)initWithData:(NSInteger *)clientId companyName:(NSString *)company companyContact:(NSString*)contact isAgent:(bool)agent parentId:(NSInteger*)parentId {
//[self = super init];
self.clientId= clientId;
self.companyName= company;
self.companyContact= contact;
self.isAgent = agent;
self.parentId = parentId;
return self;
}
And the LoginResult class is:
#interface LoginResult : NSObject {
LoginResultType result;
User *user;
NSString * const loginResultTypeArray[4];
}
#property (nonatomic, readwrite) LoginResultType result;
#property (nonatomic, retain) User *user;
Just a try: are you correctly retaining companyName and companyContatct in your User class?
EDIT:
Next thing I would check is loginResultTypeArray. How are string assigned to it? I guess that this advice will also sound trivial to you, but it is really difficult to come up with useful suggestion with so little code...
Can't you get some idea about which CFString is actually being released? If it is not an autoreleased object, possibly the stack trace could point at the method which is sending the release message... this would be very helpful...
Otherwise, I would try and NSLog some of your NSStrings addresses, so that you can compare them with the address you find in the error log (and, again, try and find out which string was actually reused after deallocation).
Finally, another approach to find out which string is used after deletion could be using method swizzling to replace NSString's dealloc with a method of yours that, before calling the swizzled dealloc, does some logging of the objec. This will produce much log info, but knowing the address of the string you could find easily what you need. Find here info about swizzling.
This was a nightmare to track down. I had a method which returned an NSString *, which was then parsed by another method to produce an XML document, then release by the second method. I actually needed to autorelease it in the first method.

#dynamic properties and its usage?

Can anyone give me clear picture about dynamic property and its usage? y not use the usual #property everywhere?
Dynamic properties are used when you don't provide an implementation at compile time, but ensure that one exists at runtime. Being a dynamic language, Objective-C can respond to messages at runtime, even if the class doesn't have an implementation at compile time.
Here's a contrived example: Let's say you have a Book class, backed by an NSMutableDictionary that contains the keys title and author. However, you want Book to respond to title and author as well, and have them as properties; title and author will grab the appropriate value from the dictionary, and setTitle: and setAuthor: will change the value stored in the dictionary. You could do so with this code:
#import <Foundation/Foundation.h>
#interface Book : NSObject
{
NSMutableDictionary *data;
}
#property (retain) NSString *title;
#property (retain) NSString *author;
#end
#implementation Book
#dynamic title, author;
- (id)init
{
if ((self = [super init])) {
data = [[NSMutableDictionary alloc] init];
[data setObject:#"Tom Sawyer" forKey:#"title"];
[data setObject:#"Mark Twain" forKey:#"author"];
}
return self;
}
- (void)dealloc
{
[data release];
[super dealloc];
}
- (NSMethodSignature *)methodSignatureForSelector:(SEL)selector
{
NSString *sel = NSStringFromSelector(selector);
if ([sel rangeOfString:#"set"].location == 0) {
return [NSMethodSignature signatureWithObjCTypes:"v#:#"];
} else {
return [NSMethodSignature signatureWithObjCTypes:"##:"];
}
}
- (void)forwardInvocation:(NSInvocation *)invocation
{
NSString *key = NSStringFromSelector([invocation selector]);
if ([key rangeOfString:#"set"].location == 0) {
key = [[key substringWithRange:NSMakeRange(3, [key length]-4)] lowercaseString];
NSString *obj;
[invocation getArgument:&obj atIndex:2];
[data setObject:obj forKey:key];
} else {
NSString *obj = [data objectForKey:key];
[invocation setReturnValue:&obj];
}
}
#end
int main(int argc, char **argv)
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
Book *book = [[Book alloc] init];
printf("%s is written by %s\n", [book.title UTF8String], [book.author UTF8String]);
book.title = #"1984";
book.author = #"George Orwell";
printf("%s is written by %s\n", [book.title UTF8String], [book.author UTF8String]);
[book release];
[pool release];
return 0;
}
Note the the methods are "created" at runtime via forwardInvocation:; hence, title and author are dynamic properties.
(This isn't the best example, but I think it gets the point across.)
#dynamic thing; is merely a way to inform the system not to generate getters/setters for the thing, that you (or someone else) will provide them for you—As in, they'll be there at runtime.
This is in contrast to #synthesize which tells the compiler to generate the getter/setter (as appropriate) for you.
#dynamic is (in my experience) used primarily in conjunction with Core Data and subclasses of NSManagedObject. To quote Marcus Zarra's Core Data,
By declaring them
[attributes/relationships], we are
telling the compiler to ignore any
warnings associated with there
properties because we "promise" to
generate them at runtime. Naturally,
if the turn up missing at runtime,
then our application is going to
crash.

Converting a NSObject into NSData

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!