Crashed while using NSKeyedArchiver - iphone

I have a NSMutableArray and NSString I want to Archive these and also want to retrieve array and string with out change data inside the array and string.
I given like this,
for Archive,
NSMutableData *data = [[NSMutableData alloc]init];
NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data];
[archiver encodeObject:arrayRandomNumberForUser forKey:#"array"];
[archiver encodeObject:stringBingo forKey:#"string"];
[archiver finishEncoding];
for UnArchive,
NSData *data=match.matchData;
NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data];
mutableArray = [unarchiver decodeObjectForKey:#"array"];
stringtakenValue = [unarchiver decodeObjectForKey:#"string"];
[unarchiver finishDecoding];
Where, match.matchData is an NSData object getting from gamecenter take turn method.
When called the take turnMethod it is crashed. Here is the Console window error message.
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: ' -[NSXPCEncoder encodeObject:forKey:]: This coder only encodes objects that adopt NSSecureCoding (object is of class 'NSKeyedArchiver').'

I not sure but I think unarchiver returns an NSArray, not an NSMutableArray. So try
mutableArray = [[unarchiver decodeObjectForKey:#"array"] mutableCopy];

Related

How to pass NSData as NSString and get it back?

i have a NSData object.
I want to convert it to a string, pass to a function and then reconvert back to NSData object.
But How?
I tried this method, but NSData value it's different from the original!
here my code:
// a generic class
APClass *c = [[APClass alloc] init];
c.aNumber = 123;
c.aString = #"my string";
// my data obj
NSMutableData *data = [NSMutableData data];
// archiver to store class in nsdata
NSKeyedArchiver *encoder = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data];
[encoder encodeObject:[NSNumber numberWithInt:c.aNumber] forKey:#"aNum"];
[encoder encodeObject:c.aString forKey:#"aStr"];
[encoder finishEncoding];
[encoder release];
[c release];
NSLog(#"%#", data);
NSString *d = [NSString stringWithFormat:#"%#", data];
// ---
NSString *strFromData = [NSString stringWithFormat:#"%#", d];
NSData *dataNM = [strFromData dataUsingEncoding:NSUTF8StringEncoding];
// decoder to retrieve class from nsdata
NSKeyedUnarchiver *decoder = [[NSKeyedUnarchiver alloc] initForReadingWithData:dataNM];
int number = [[decoder decodeObjectForKey:#"aNum"] intValue];
NSString *string = [decoder decodeObjectForKey:#"aStr"];
[decoder finishDecoding];
[decoder release];
NSLog(#"[Number: %d] -- [String: %#]", number, string);
How can i convert back to original NSData?
data and dataNM are different in size.
Compiler give back this error:
2012-04-02 16:33:28.269 DataTest[18008:f803] -[__NSCFData
objectForKey:]: unrecognized selector sent to instance 0x6b46c80
2012-04-02 16:33:28.270 DataTest[18008:f803] * Terminating app due
to uncaught exception 'NSInvalidArgumentException', reason:
'-[__NSCFData objectForKey:]: unrecognized selector sent to instance
0x6b46c80'
thanks.
Solved.
Using dataUsingEncoding, value of NSData it's different.
To pass data around methods or apps, etc, i've used base64 convertion.
Encode
NSString *d =
[NSString stringWithFormat:#"appdue://obj=%#",
[APBase64Converter base64forData:data]];
Decode
NSData *data = [APBase64Converter base64DataFromString:urlParams];
APBase64Converter Is a lib that encode/decode easily data and strings.
Working example and APBase64Converter lib can be downloaded from here: http://goo.gl/8YNjS
Thanks all.
I "retain" this post for helps people and me next time!
Use
NSData to NSString:
- (id)initWithData:(NSData *)data encoding:(NSStringEncoding)encoding
and
NSString to NSData:
- (NSData *)dataUsingEncoding:(NSStringEncoding)encoding
with encoding
NSUTF8StringEncoding
or
NSMacOSRomanStringEncoding (which is an 8-bit encoding).
Use Base64 class it is available in github
DatabaseResult* result = [self.database executeQuery:#"SELECT * FROM `images` WHERE image_id = ? AND imagetype = ?", self.routeId,self.imagetype];
for(EGODatabaseRow* row in result) {
NSString *strImage=[row stringForColumn:#"image_photo"];
[Base64 initialize];
NSData *data = [Base64 decode:strImage];
UIImage *image=[UIImage imageWithData:data];
}
This is how i convert String to data and then data to image.

Passing archived Data through a sharedURL

I currently have two somewhat similar apps and was looking into a way to share data across the two apps. I set up a shared url and everything and can jump from one app to the other without any problem. The problem arises when I try to pass data through the url. I have a class in both apps that are identical to one another and am relying on a NSKeyedArchiver and decoder for the encoding of them and decoding as well as encoding them in strings to pass along the url.
Having access to both sides I know that the data passed from app 1 to app 2 is untainted and arrives just as app 1 encoded it. But I can't seem to decode the data on the other side!
I encode it here:
NSURL* app2=[NSURL URLWithString:#"app2://"];
if ([[UIApplication sharedApplication] canOpenURL:app2]) {
NSMutableData *data = [[NSMutableData alloc] init];
NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data];
[archiver encodeObject:[tableHelper objectAtIndex:[indexPath section]]forKey:#"TempData" ];
[archiver finishEncoding];
NSString* tmp=[[[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding ] autorelease];
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:[NSString stringWithFormat:#"app2://%#",[tmp stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]]];
[data release];
[archiver release];
}
I then decode it as such:
- (BOOL)application:(UIApplication *)application handleOpenURL:(NSURL *)url
{
if (!url) {
return NO;
}
NSString *URLString = [url absoluteString];
NSString* dataString=[[URLString componentsSeparatedByString:#"app2://"] lastObject];
NSString* perString=[dataString stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSKeyedUnarchiver* unArchiver=[[NSKeyedUnarchiver alloc] initForReadingWithData:[perString dataUsingEncoding:NSASCIIStringEncoding]];
TempItem* temp=[unArchiver decodeObjectForKey:#"TempData"];
[unArchiver finishDecoding];
//...//
}
Is there more to encoding/decoding custom classes that I am missing? I do implement NSCoding in my class the problem I think is in
[perstring dataUsingEncoding:NSASCIIStringEncoding]
as it appears to always be null?
Thanks!
I checked the output after the encoding and it is as such
bplist00Ô,-T$topX$objectsX$versionY$archiverÑXTempData©
'U$nullÖ
XitemsizeXquantityUpriceV$classYconvertedTunitQ1#?ð
and after adding percent escapes
bplist00%C3%94%01%02%03%04%05%08,-T$topX$objectsX$versionY$archiver%C3%91%06%07XTempData%C2%80%01%C2%A9%09%0A%17%18%19%1A%1B%1F'U$null%C3%96%0B%0C%0D%0E%0F%10%11%12%13%14%15%16XitemsizeXquantityUpriceV$classYconvertedTunit%C2%80%03%C2%80%05%C2%80%04%C2%80%08%C2%80%06%C2%80%02Q1%23?%C3%B0%00%00%00%00%00%00%23?%C3%B0%00%00%00%00%00%00%10%01%C3%92%1C%0E%1D%1EZNS.objects%C2%A0%C2%80%07%C3%92%20!%22&X$classesZ$classname%C2%A3%23$%25%5ENSMutableArrayWNSArrayXNSObject%5ENSMutableArray%C3%92%20!(+%C2%A2)*XTempItemXNSObjectXTempItem%12%00%01%C2%86%C2%A0_%10%0FNSKeyedArchiver%00%08%00%11%00%16%00%1F%00(%002%005%00%3E%00#%00J%00P%00%5D%00f%00o%00u%00%7C%00%C2%86%00%C2%8B%00%C2%8D%00%C2%8F%00%C2%91%00%C2%93%00%C2%95%00%C2%97%00%C2%99%00%C2%A2%00%C2%AB%00%C2%AD%00%C2%B2%00%C2%BD%00%C2%BE%00%C3%80%00%C3%85%00%C3%8E%00%C3%99%00%C3%9D%00%C3%AC%00%C3%B4%00%C3%BD%01%0C%01%11%01%14%01%1D%01&%01/%014%00%00%00%00%00%00%02%01%00%00%00%00%00%00%00.%00%00%00%00%00%00%00%00%00%00%00%00%00%00%01F
I added NSLogs on the decode portion and it is the same. I am baffled...
After fixing the misplacement of the [unArchiver finishdecoding]; I now get the error
*** -[NSKeyedUnarchiver initForReadingWithData:]: data is NULL

Cast JSON object (NSCFDictionary) to string and parse it

I'm working with facebook connect and trying to handle the JSON object that i'm receiving.
I invoked the requstWithGraphPath method and need to get back a JSON object,
tried to parse it and getting an error:
SBJSON *parser = [[SBJSON new] autorelease];
NSData *data = [[NSData alloc] initWithData:result];
NSString *jsonString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; -> in this line - "[__NSCFDictionary length]: unrecognized selector sent to instance"
NSArray *events = [parser objectWithString:jsonString];
What's the problem?
Can I get the string in an other way or parse the object differently?
Thanks.
If you are working with the delegate callback
- (void)request:(FBRequest *)request didLoad:(id)result;
the parsing work has been done for you. Traverse the NSDictionary or NSArray to find the data you are looking for. If you are working with the delegate callback
- (void)request:(FBRequest *)request didLoadRawResponse:(NSData *)data;
you should initialize an NSString with the data, and use the category method that SBJSON adds to NSString for creating an id. That is assuming the data is data that constructs a string.
NSString *jsonString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
id result = [jsonString JSONValue];
Are you sure the error happens on that line, or does it happen on the line above?
If result is an NSDictionary (or CFDictionary, same thing), then it is already parsed and you do not need to do that yourself — and it could cause that error message too, on the line above.
The line:
data = [[NSData alloc] initWithData:result];
is almost certainly not what you want to do, as it is equivalent to
data = [result copy];
assuming that result is an NSData object (or NSMutableData), which I'm guessing it isn't.

Is it possible to store multiple objects using object archving?

Okay, of course it is possible, that's no issue. In my code I have multiple objects that I work with, and they need to be data persistent. To do that I've used object encoding and storing them in a data file. Such as:
-(NSString *)dataFilePath
{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
return [documentsDirectory stringByAppendingPathComponent:kFilename];
}
And then further along in another section of code:
NSMutableData *data = [[NSMutableData alloc] init];
NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data];
NSLog(#"Test 4");
[archiver encodeObject:playerTwo forKey:#"PlayerTwoObject"];
[archiver encodeObject:singlePlayer forKey:#"SinglePlayer"];
[archiver encodeObject:playerOne forKey:#"PlayerOneObject"];
[archiver encodeObject:settings forKey:#"Settings"];
[archiver finishEncoding];
[data writeToFile:[self dataFilePath] atomically:YES];
[singlePlayer release];
[playerOne release];
[playerTwo release];
[settings release];
And that saves the objects. I know it works, because I can take them out with this code:
unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:[[NSMutableData alloc] initWithContentsOfFile:[self dataFilePath]]];
SettingsObject *settings = [unarchiver decodeObjectForKey:#"Settings"];
Player *test = [unarchiver decodeObjectForKey:#"PlayerOneObject"];
NSLog(#"Power On Settings Test: %d,%d", [settings playerOneStartingLife], [settings singlePlayerStartingLife]);
NSLog(#"Power On Player Test: %#,%d", [test name], [test life]);
Cool! So now I can store my objects, and read them out. So this is where the issue comes in. In a different class I take out one of the objects I'm storing and work with it. Not all of them, I only read out one. And then I save it again after I change the settings. But I only save it:
NSMutableData *data = [[NSMutableData alloc] init];
NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data];
[archiver encodeObject:singlePlayer forKey:#"SinglePlayer"];
[archiver finishEncoding];
[data writeToFile:[self dataFilePath] atomically:YES];
[singlePlayer release];
After I have done that, all the rest of the objects I have stored are removed. Now I'm confused, because I thought I was only editing the one key "SinglePlayer", but apparently that somehow removes the rest of them? Is that supossed to happen? I have had some issues with releasing the archiver, would that affect it at all... I don't think it would. Does anyone have some insight on this, because I don't want to have to take all of the objects out and re-encode them if I only want to edit one of them...
Thanks for your help!
You create a new archiver, with an emtpy data object. You write the SinglePlayer object in the data, but you do not store the rest of the data in the same, newly created archive.
To do so, unarchive the whole previously stored archive, then adjust the SinglePlayer object and archive and save it again.

Objective-c way of serializing arrays that contain different types

My NSMutableArray instance contains instances of different types of objects that have common ancestor. I want to serialize the array, but NSKeyedArchiver class seems to archive arrays contain certain types of objects. Is there an easy way to to this with stock serialization classes ?
I don't think NSKeyedArchiver is your problem, since it doesn't know about arrays or anything. Perhaps you need to implement the NSCoding protocol on each subclass?
Edit:
Not sure how you're using your NSKeyedArchiver, but this is how you should use it:
NSMutableData *data = [NSMutableData new];
NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data];
[archiver encodeObject:array forKey:#"array"];
[data writeToFile:filename attomically:YES];
[archiver release];
[data release];
And to read it in later:
NSData *data = [[NSData alloc] initWithContentsOfFile:filename];
NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data];
array = [[unarchiver objectForKey:#"array"] retain];
[unarchiver release];
[data release];
This assumes that array is some type of NSObject, including NSArray, NSSet, NSDictionary, etc, or any of their mutable equivalents.