Error when encoding UITouchesEvent as NSData - ios

I have an NSDictionary containing some objects: an NSSet of UITouches, a UIEvent, and an NSString.
When I try to encode the dictionary to NSData, the string encodes properly. I had an error with the UITouches being encoded, but I found a way to extend the class with some code so that a UITouch can be encoded. However, I still can't encode the UIEvent (which is actually a UITouchesEvent). How can I extend the UIEvent or UIInternalEvent to make them encodable to NSData?
Methods I use for encoding/decoding:
-(NSString *)stringFromDictionary:(NSDictionary *)dict{
NSMutableData *data = [[NSMutableData alloc] init];
NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data];
[archiver encodeObject:dict forKey:#"dictKey"];
[archiver finishEncoding];
return [Base64 encode:data];
}
-(NSDictionary *)dictionaryFromString:(NSString *)string{
NSData *data = [[NSMutableData alloc] initWithData:[Base64 decode:string]];
NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data];
NSDictionary *myDictionary = [unarchiver decodeObjectForKey:#"dictKey"];
[unarchiver finishDecoding];
return myDictionary;
}
Error I get:
-[UITouchesEvent encodeWithCoder:]: unrecognized selector sent to instance
Please let me know if I'm missing any important info regarding debugging. Thanks!

You have to have UITouch or UITouchesEvent adapt the UICoding protocol. This means it must support these methods:
- (id)initWithCoder:(NSCoder *)decoder;
- (void)encodeWithCoder:(NSCoder *)encoder;
I haven't tried this myself, but it should work if you do this in a class category. The difficulty will be to find out what you need to encode so it can be decoded again into a correct instance, if that's what you need.

Related

Appending data to a string without losing previous data

I have this is on the top of my program:
#property (strong, nonatomic) NSMutableData *data;
I thought this would allow me to store the value from every time this method runs:
- (void)peripheralManager:(CBPeripheralManager *)peripheral didReceiveWriteRequests:(NSArray *)requests
{
for (CBATTRequest *request in requests) {
NSString *stringValue = [[NSString alloc] initWithData: [request value] encoding:NSUTF8StringEncoding];
// Have we got everything we need?
if ([stringValue isEqualToString:#"EOM"]) {
// We have, so show the data,
[self.textview setText:[[NSString alloc] initWithData:self.data encoding:NSUTF8StringEncoding]];
}
// Otherwise, just add the data on to what we already have
[self.data appendData:[request value]];
}
}
This method waits for a write request to be received and stores the value in a string. I have a core bluetooth central that is sending three blocks of data. This is to get around the data transfer size restriction within bluetooth LE. The problem is I can't get the three values stored. I am trying to not just store the last value but add the new value to the end of a nsstring or nssdata every time the method is called. Any help would be greatly appreciated. I thought the property at the top would allow me to do it but it either only stores the last value or nothing at all. I am not used to the ways of objective c yet. Thanks.
Even this doesn't write anything to self.data:
NSString * result = [[requests valueForKey:#"value"] componentsJoinedByString:#""];
NSData* data = [result dataUsingEncoding:NSUTF8StringEncoding];
[self.data appendData:data];
// Log it
NSLog(#"%#",self.data);
You should use NSMutableArray instead of NSString as a mutable string.
- (void)peripheralManager:(CBPeripheralManager *)peripheral didReceiveWriteRequests:(NSArray *)requests
{
NSMutableString *stringValue = [[NSMutableString alloc] init];
for (CBATTRequest *request in requests) {
[stringValue appendString:[[NSString alloc] initWithData:[request value] encoding:NSUTF8StringEncoding]];
// Have we got everything we need?
if ([stringValue isEqualToString:#"EOM"]) {
// We have, so show the data,
[self.textview setText:[[NSString alloc] initWithData:self.data encoding:NSUTF8StringEncoding]];
}
// Otherwise, just add the data on to what we already have
[self.data appendData:[request value]];
}
Remember kids when dealing with NSMutableData always initialize it!
_data = [[NSMutableData alloc] init];
This fixed the null problem for me.

How to access objects from NSMutableData

I have a NSMutableArray and a NSString . These two are archived to NSData and add to a NSMutableData Object.
How can I access each data from NSMutableData Object.
NSData *dataArray= [NSKeyedArchiver archivedDataWithRootObject:mutableArray];
NSData *dataTouchedNumer=[NSKeyedArchiver archivedDataWithRootObject:stringValue];
NSMutableData *mutableData=[[NSMutableData alloc]init];
[mutableData appendData:dataArray];
[mutableData appendData:dataTouchedNumer];
You can't do this the way you are showing. If you append two NSData objects together into a single mutable data object, there is no way to separate them later. Try this instead:
To archive the two objects:
NSMutableArray *mutableArray = ... // your mutable array
NSString *stringValue = ... // your string
NSMutableData *data = [NSMutableData data];
NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data];
[archiver encodeObject:mutableArray forKey:#"array"];
[archiver encodeObject:stringValue forKey:#"string"];
At this point, data contains the two objects. Do what you need with the data (save it for example).
To get your objects back:
NSData *data = ... // the archived data
NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data];
NSMutableArray *mutableArray = [unarchiver decodeObjectForKey:#"array"];
NSString *stringValue = [unarchiver decodeObjectForKey:#"string"];
According to the docs, "archivedDataWithRootObject: returns an NSData object containing the encoded form of the object graph whose root object is given." So your mutableData object contains 2 such encoded object graphs. The question is what kind of data you want to read out of mutableData. It probably does not make much sense, to read simply all bytes with [mutableData bytes], or part of it with getBytes:length: or getBytes:range:.

Can I Archive NSData objects inside another NSData without corrupting data?

I have two NSData objects I want to store within a third NSData object. The idea is I want to make it easy when I later decode the larger object, to get at the two smaller objects independently of one another, without worrying about their relative sizes or datatypes.
It appears the best way to do this is to use NSKeyedArchiver to create a sort of root-level key-value structure within the larger NSData object, in which I can store the two smaller objects within separate keys. That's what I've attempted to do here:
NSData *data1Before = [#"abcdefgh" dataUsingEncoding:NSUTF8StringEncoding];
NSData *data2Before = [#"ijklmnop" dataUsingEncoding:NSUTF8StringEncoding];
NSMutableData *allData = [[NSMutableData alloc] init];
NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:allData];
[archiver encodeObject:data1Before forKey:#"key1"];
[archiver encodeObject:data2Before forKey:#"key2"];
[archiver finishEncoding];
NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:allData];
NSData *data1After = [unarchiver decodeObjectForKey:#"key1"];
NSData *data2After = [unarchiver decodeObjectForKey:#"key2"];
[unarchiver finishDecoding];
NSString *string1After = [NSString stringWithUTF8String:[data1After bytes]];
NSString *string2After = [NSString stringWithUTF8String:[data2After bytes]];
NSLog(#"after1: %#",string1After);
NSLog(#"after2: %#",string2After);
The problem is, when you run this code over and over, you get all sorts of different results coming from the NSLog statements- someetimes special characters get appended to the end of the strings, sometimes they're just NULL.
It appears this corruption has something to do with this "double-encoding" process I'm using. When I modify the code so that the NSKeyedArchiver just calls encodeObject directly on NSStrings, rather than on NSData objects, I can later use decodeObjectForKey and get at those strings without any problems- no corruption at all.
Is there a better way of doing this than using NSKeyedArchiver? Or am I using it incorrectly?
Thanks to rmaddy for his answer above- I just needed to replace this:
[NSString stringWithUTF8String:[data1After bytes]];
with this:
[[NSString alloc] initWithData:data1After encoding: NSUTF8StringEncoding];
and that fixed it.

Key not found in an NSKeyedUnarchiver object

I usually encode my data in a NSFileWrapper like this (I leave out the NSFileWrapper bit):
-(NSData*)encodeObject:(id<NSCoding>)o {
#autoreleasepool {
NSMutableData *data = [NSMutableData data];
NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data];
[archiver encodeObject:o forKey:#"data"];
[archiver finishEncoding];
return data;
}
}
And I usually get my data back when doing this:
- (id)decodeObjectFromWrapperWithPreferredFilename:(NSString *)p {
NSFileWrapper *wrapper = [self.fileWrapper.fileWrappers objectForKey:p];
if (!wrapper) {
NSLog(#"Unexpected error: Couldn't find %# in file wrapper!", p);
return nil;
}
NSData *data = [wrapper regularFileContents];
NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data];
NSLog(#"%#", [unarchiver decodeObjectForKey:#"data"]);
return [unarchiver decodeObjectForKey:#"data"];
}
Sometimes, I get NSData back (it is not nil), but [unarchiver decodeObjectForKey:#"data"] will return NIL. It appears as if there is no object for the key #"data" even though there should be. I guess something must have gone wrong when encoding, but I'm not sure how to trouble shoot this. Can I just take whatever is in data and encode it, not worrying if it has got the right key? I mean there should only ever be one key "data".
Why is your code so complicated :) The NSKeyedArchiver class has helper methods that will do what you want more simply :
// to turn an object into NSData
return [NSKeyedArchiver archivedDataWithRootObject:o];
// To turn NSData into your object again
return [NSKeyedUnarchiver unarchiveObjectWithData:data];

NSCoding NSKeyedUnarchiver unarchiveObjectWithFile: returning null

I am trying to save some values from my app using NSCoding. I'm able to save the value but not able to retrieve it.
Here's where I am declaring the protocol:
#interface AddReminderEventViewController : UIViewController <UIPickerViewDelegate, UIPickerViewDataSource, NSCoding>
Here's where I'm complying with the protocol:
-(void)encodeWithCoder:(NSCoder *)enCoder
{
[enCoder encodeObject:self.eventRepeatDurationDate forKey:kEventRepeatDuration];
[enCoder encodeObject:self.eventIDsMutableArray forKey:kEventIDsMutableArray];
[enCoder encodeObject:self.eventRepeatDurationString forKey:#"mytest"];}
and here:
-(id)initWithCoder:(NSCoder *)decoder {
if (self = [super init]){
self.eventRepeatDurationDate = [[decoder decodeObjectForKey:kEventRepeatDuration] retain];
self.eventIDsMutableArray = [[decoder decodeObjectForKey:kEventIDsMutableArray] retain];
self.eventRepeatDurationString = [[decoder decodeObjectForKey:#"mytest"] retain];} return self; }
and here's where I call the methods to do the archiving and unarchiving:
[self saveDataToDisk];
[self loadDataFromDisk];
and here are the bodies of these methods and it's NSLog contents:
- (void)saveDataToDisk {
NSString *reminderEventIDsPathString = #"~/Library/Application Support/ReminderIDs.archive";
//reminderEventIDsPathString = #"~/Library/Application Support/ReminderIDs.archive";
reminderEventIDsPathString = [reminderEventIDsPathString stringByExpandingTildeInPath];
NSLog(#"WATCH1: reminderEventIDsPathString is %#", reminderEventIDsPathString);
NSMutableDictionary *rootObject;
rootObject = [NSMutableDictionary dictionary];
[rootObject setValue:eventRepeatDurationString forKey:#"mytest"];
NSLog(#"1rootObject IS %#", rootObject);
[NSKeyedArchiver archiveRootObject:rootObject toFile:reminderEventIDsPathString];}
reminderEventIDsPathString is /Users/tester/Library/Application Support/iPhone Simulator/5.0/Applications/E26D57DE-C4E1-4318-AEDD-7207F41010A9/Library/Application Support/ReminderIDs.archive
2012-01-16 15:47:48.578 [29658:15503] 1rootObject IS {mytest = 7;}
and here is the unarchiver code along with its NSLog contents:
- (void)loadDataFromDisk {
NSString *testValue = [[NSString alloc] init];
NSString *reminderEventIDsPathString = #"~/Library/Application Support/ReminderIDs.archive";
reminderEventIDsPathString = [reminderEventIDsPathString stringByExpandingTildeInPath];
NSLog(#"WATCH2: reminderEventIDsPathString is %#", reminderEventIDsPathString);
NSMutableDictionary *rootObject;
rootObject = [[NSKeyedUnarchiver unarchiveObjectWithFile:reminderEventIDsPathString] retain];
NSLog(#"2rootObject IS %#", rootObject);
NSLog(#"WATCH3 - %#", [rootObject objectForKey:#"mytest" ]);
if ([rootObject valueForKey:#"mytest"]) {
testValue = [rootObject valueForKey:#"mytest"];
NSLog(#"WATCH: testValue is %#", testValue); } }
2012-01-16 15:48:14.965 [29658:15503] WATCH2: reminderEventIDsPathString is /Users/tester/Library/Application Support/iPhone Simulator/5.0/Applications/E26D57DE-C4E1-4318-AEDD-7207F41010A9/Library/Application Support/ReminderIDs.archive
2012-01-16 15:48:17.879 [29658:15503] 2rootObject IS (null)
What am I missing that I'm not able to unarchive the contents? I'm just focusing on the easiest of the values in my encoder/decoder methods just to test it but I'm not even able to get the string value to work.
Thanks
The path where you save and load your reminder is wrong. Maybe replace to this
NSString *reminderEventIDsPathString = [[NSHomeDirectory() stringByAppendingPathComponent:#"Documents"] stringByAppendingPathComponent:#"ReminderIDs.archive"];

Resources