Struct to NSData between Mac and iOS - ios

I have been working on this problem for a while, I have an app running on the mac, it has co-ordinate data stored in a struct like this:
struct xyz {
float x;
float y;
float z;
};
struct xy {
float x;
float y;
};
struct object {
struct xyz *myXYZ;
struct xy *myXY;
};
This all works as expected, then I add the struct into NSData like so:
struct object anInitialTestStruct;
NSMutableData *myTestDataOut = [NSMutableData dataWithBytes:&anInitialTestStruct length:64 freeWhenDone:NO];
BOOL good = [myTestDataOut writeToFile:[NSString stringWithFormat:#"%#/filename.dat", docsDirectory] atomically:YES];
This works as expected, I get a file and looks like there is data in it (for reference I have used pointers and malloc for the anInitialTestStruct but still don't get the desired result)
Now on the iphone, I copy the file into the project, and do this:
NSString *filePath = [[NSBundle mainBundle] pathForResource:#"filename" ofType:#"dat"];
NSData *myVecNSData = [[NSData alloc] initWithContentsOfFile:filePath options:NSDataReadingUncached error:&error];
if ( error ) {
NSLog(#"%#", error);
}
I don't get the correct data back. Interestingly if I run the initWithContents method on the mac and read the file in there it appears to be ok.
So I'm thinking there is something different on the iphone / mac way it deals with the filesystem.... I've tried encoding the data using NSKeyedArchiver, but I get an exception stating "incomprehensible archive....."

For case of your "object" structure you have to store "xy" and "xyz" structures separately, for example in a dictionary:
struct object anInitialTestStruct;
NSDictionary *structureDataAsDictionary = [NSDictionary dictionaryWithObjectsAndKeys:
[NSMutableData dataWithBytes:anInitialTestStruct.myXY length:sizeof(xy)], #"xy key",
[NSMutableData dataWithBytes:anInitialTestStruct.myXYZ length:sizeof(xyz)], #"xyz key",
nil];
NSData *myTestDataOut = [NSKeyedArchiver archivedDataWithRootObject:structureDataAsDictionary];
BOOL good = [myTestDataOut writeToFile:[NSString stringWithFormat:#"%#/filename.dat", docsDirectory] atomically:YES];
and decoding is something like this:
struct object anInitialTestStruct;
NSString *filePath = [[NSBundle mainBundle] pathForResource:#"filename" ofType:#"dat"];
NSData *myVecNSData = [[NSData alloc] initWithContentsOfFile:filePath options:NSDataReadingUncached error:&error];
if ( error ) {
NSLog(#"%#", error);
}
// retrieving dictionary from NSData
NSDictionary *structureDataAsDictionary = [NSKeyedUnarchiver unarchiveObjectWithData:myVecNSData];
// allocating memory for myXY and myXYZ fields
anInitialTestStruct.myXY = (xy*)malloc(sizeof(xy));
if (anInitialTestStruct.myXY == NULL) {
// error handling
}
anInitialTestStruct.myXYZ = (xyz*)malloc(sizeof(xyz));
if (anInitialTestStruct.myXYZ == NULL) {
// error handling
}
// filling myXY and myXYZ fields with read data
[[structureDataAsDictionary objectForKey:#"xy key"] getBytes:anInitialTestStruct.myXY];
[[structureDataAsDictionary objectForKey:#"xyz key"] getBytes:anInitialTestStruct.myXYZ];

You might have truble encoding your pointers see here
"Pointers
You can’t encode a pointer and get back something useful at decode time. You have to encode the information to which the pointer is pointing. This is true in non-keyed coding as well. ..."

Related

WatchConnectivity attempting to send data to Watch app

I'm trying to send data from my iOS app to the Watch app. I'm using the updateApplicationContext for this.
I have a json file that I converted in NSDictionary and tried to send it. But there is an error, here it is:
[WCSession updateApplicationContext:error:]_block_invoke failed due to WCErrorCodePayloadUnsupportedTypes
The file is correctly read.
Now here is the code that tries to send the data.
NSString *fileName = [[NSBundle mainBundle] pathForResource:#"data"
ofType:#"json"];
NSLog(fileName);
if (fileName) {
NSData *jsonData = [[NSData alloc] initWithContentsOfFile:fileName];
NSDictionary *data = [NSJSONSerialization JSONObjectWithData:jsonData
options:0
error:&error];
if (error) {
NSLog(#"Something went wrong! %#", error.localizedDescription);
}
else {
NSLog(#"Rsens info: %#", data);
[WatchSessionManager.sharedManager updateApplicationContextWithApplicationContext:#{#"json": data} error:&error];
}
}
else {
NSLog(#"Couldn't find file!");
}
I read somewhere that the types that we could send were limited, but the dictionary was allowed. I'm sending a dictionary though.
Can you find what is the error?
[SOLUTION]
I found out that there were values of type long in my dictionary. In my JSON I had some properties that were transtyped in type long. Here is one of the properties before:
"state": 0
I just put my numbers in string quotes.
"state":"0"
Check the dictionary's content.
WatchConnectivity dictionaries can only contain property list types.

Corruption of NSString or encoding issue in Objective C

Please see code below:
+ (void)splashDataFromJSON:(NSData *)objectNotation error:(NSError **)error
{
NSError *localError = nil;
NSDictionary *parsedObject = [NSJSONSerialization JSONObjectWithData:objectNotation options:0 error:&localError];
if (localError != nil) {
*error = localError;
}
NSMutableArray* btms = [[NSMutableArray alloc] init];
NSMutableDictionary* btmManufacturerResolutionDictionary = [[BTMCache sharedManager] btmManufacturerResolutionDictionary];
NSArray *results = [parsedObject valueForKey:#"results"];
NSLog(#"Count %d", parsedObject.count);
NSString* imageBaseUrl = [[parsedObject valueForKey:#"general"] valueForKey:#"image_base_url"];
imageBaseUrl = [imageBaseUrl stringByAppendingString:#"hdpi/"];
NSString* splashImageName = [[[parsedObject valueForKey:#"general"] valueForKey:#"splash"] valueForKey:#"img"];
NSString* splashAdvertiserURL = [[[[parsedObject valueForKey:#"general"] valueForKey:#"splash"] valueForKey:#"url"] copy];
NSMutableString* appendedString = [[NSMutableString alloc] init];
for(int i =0 ;i<[splashAdvertiserURL length]; i++) {
char character = [splashAdvertiserURL characterAtIndex:i];
printf(&character);
sleep(0.1);
if (character != "!")
{
[appendedString appendFormat:#"%c", character];
}
}
[[SplashData sharedManager] setSplashAdvertiserURL:appendedString];
[[SplashData sharedManager] setSplashImageName:splashImageName];
splashAdvertiserURL = [[SplashData sharedManager] splashAdvertiserURL];
}
The point of interest is in splashAdvertiserURL. When I receive this data and print it out using po, it comes out as "https://radar.com/ref/go/84/". This is fine and what was expected. When I look at the incoming data in JSONLint it looks like this:
"general": {
"image_base_url": "https:\/\/radar.com\/img\/manufacturers\/",
"splash": {
"img": "image1.png",
"url": "https:\/\/radar.com\/ref\/go\/84\/"
}
},
As you can see, further on I put the NSString into a singleton with an NSString property. Nothing abnormal here. I then proceed to retrieve it to see that all is ok. Further to this the program continues. In another class I wish to retrieve this information, and when I try and do that, it throws EXC_BAD_ACCESS. There appears to be garbage in there.
I then put in a loop in the code as you can see to print out the characters one at a time. Very curiously, when I print that out using po I get:
https://
r
a
d
ar.com/ref/go/8 4!/"
Exactly in that format. If I then proceed to hardcode the string https://radar.com/ref/go/84/ - including escape characters and everything, then all works fine. No issues. If I handle a normal string incoming without escape characters it stores fine in the singleton as well, no issue. enter code here
I am pretty stumped here as to what is going on. Can someone assist?
Thank you
For URL you received as string you need to encode before use it to in your app. Have a look at below code:
NSString *sampleUrl = #"https:\/\/radar.com\/ref\/go\/84\/";
NSString *encodedUrl = [sampleUrl stringByAddingPercentEscapesUsingEncoding:
NSUTF8StringEncoding];

Compare NSData with byte sequence?

Note: in other questions they compare a value stored in NSData objects, not its bytes.
I want to perform something like this:
NSData *d = ...;
if (d == "fff1") {
...
}
The only solution I have found:
NSData *d = ...;
NSString *str = [NSString withFormat:#"%#", d];
if ([str isEqualToString:#"<fff1>"] {
...
}
But I don't like that I need to add extra surrounding backets in comparison. Are there better solutions?
For purpose of comparing raw data you use memcmp:
NSData *dataA;
void *someBuffer;
if(memcmp([dataA bytes], someBuffer, dataA.length) == 0) ; //they are the same
Note you should watch that length is not too large for any of the buffers.
EDIT: added NSData procedure:
Or better yet you could convert your string to NSData and do the comparison on the NSData:
NSData *d = ...;
if([d isEqualToData:[NSData dataWithBytes:"fff1" length:sizeof("fff1")]]) {
}

Parsing JSON object for iOS application

Currently, I have within my iPhone app a URL with which contains a JSON object that I must parse.
I am able to fetch the JSON object, convert the object to an NSString, now the issue is parsing the object/NSString object.
I am currently using SBJSON.
How do I go about iterating through the key elements of the JSON object using SBJSON's framework?
{
"status":"SUCCESS",
"fields":[
{
"type":"instant",
"field":"GenerationPower",
"label":"now",
The JSON object is MUCH larger than just these keys and key elements but once this issue is resolved, I'm sure the rest of the JSON object will be easy since i'll have a reference.
Thank you Stackoverflow!
EDIT:
Here's some code to clarify my issue.
+ (NSString *) pullingInfo
{
NSURL *solarWebURL = [NSURL URLWithString:myurl];
if (solarWebURL)
{
NSLog(#"Calling: %#", solarWebURL);
NSData *jsonData = [NSData dataWithContentsOfURL:solarWebURL];
NSString *jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
return jsonString;
}
NSString* errorMessage = #"Error reading URL";
return errorMessage;
}
+ (NSDictionary *) jsonDictionaryObject
{
NSDictionary * jsonDictionary = [[NSDictionary alloc] init];
NSString * monroeString = [MonroeParser pullingInfo];
return jsonDictionary;
}
So as I said before, I have already loaded the JSON object into an NSString object "jsonString". Now I would like to start parsing the string.
I figure I may not even need to use JSON's framework for parsing, I can probably just parse the NSString using NSString conventions provided by Apple.
Any idea's? But maybe this isn't efficient....
Sine you are using SBJSON, why are you even converting the NSData to an NSString? You can use -objectWithData method for SBJSONParser to directly read the NSData into an NSDictionary.
http://sbjson.org/api/3.2/Classes/SBJsonParser.html#//api/name/objectWithData:
Let pullingInfo return an id. And in you calling function check if the id is of type NSDictionary or NSArray and parse accordingly.

RNCryptor not working with JSON string

Here are my method's to use RNCryptor to encrypt/decrypt a JSON string that I am sending to the web service. I am using a static IV variable which may be bad practice but please don't focus on that. Here is how I'm doing it:
Note: I'm using Matt Gallagher's NSData+Base64 category found here (at bottom of page)
-(NSString*)encryptString:(NSString*)plaintext withKey:(NSString*)key error:(NSError**)error{
NSData *data = [plaintext dataUsingEncoding:NSUTF8StringEncoding];
NSData *encryptionKey = [NSData dataFromBase64String:key];
NSData *IV = [NSData dataFromBase64String:ENCRYPTION_IV];
RNCryptorEngine *engine = [[RNCryptorEngine alloc] initWithOperation:kCCEncrypt settings:kRNCryptorAES256Settings key:encryptionKey IV:IV error:error];
[engine addData:data error:error];
NSData *encryptedData = [engine finishWithError:error];
NSString *based64Encrypted = [encryptedData base64EncodedString];
NSLog(#"Encrytped: %#", based64Encrypted);
return based64Encrypted;
}
-(NSString*) decryptString:(NSString*)cipherText withKey:(NSString*)key error:(NSError**)error;{
NSData *data = [NSData dataFromBase64String:cipherText];
NSData *encryptionKey = [NSData dataFromBase64String:key];
NSData *IV = [NSData dataFromBase64String:ENCRYPTION_IV];
RNCryptorEngine *engine = [[RNCryptorEngine alloc] initWithOperation:kCCDecrypt settings:kRNCryptorAES256Settings key:encryptionKey IV:IV error:error];
[engine addData:data error:error];
NSData *decryptedData = [engine finishWithError:error];
NSString *decryptedString = [[NSString alloc] initWithData:decryptedData encoding:NSUTF8StringEncoding];
NSLog(#"Decrypted: %#", decryptedString);
return decryptedString;
}
When I use a string like hello world it works fine. When I use a string like {"username":"developer","password":"abcdefG*12"} I imagine it hase something to do with the encoding but I really know what to use.
when I encrypt that string I get a base64 string and when I try to decrypt that I get an empty string.
UPDATE
It looks like it's failing because of the : in the json string.
What's weirder is it only fails with the string is in json format, I thought it was the : cause I tried that first but upon further investigation if I broke any of the JSON requirements ,'s {'s }'s it stopped working. It works with the RNEncryptor however so I'm not sure what I'm doing wrong. Either way, I think we may redesign the current flow
UPDATE 2
Here is where I am calling these methods:
NSDictionary *credentials = #{#"username":#"developer",#"password":#"abcdefG*12"};
NSString *jsonString = [ credentials JSONStringWithOptions:JKSerializeOptionNone error:&error];
NSLog(#"json string: %#", jsonString); //OUTPUTS: {"username":"developer","password":"abcdefG*12"}
CCGEncryption *encryptionObject = [[CCGEncryption alloc] init]; //THIS IS THE OBJECT WHERE THE encrypt/decrypt methods are
NSString *encrypted = [encryptionObject encryptString:jsonString withKey:ENCRYPTION_KEY error:&error];
if(error){
NSLog(#"Error:%#", error); //NO ERROR
}
NSString *decrypted = [encryptionObject decryptString:encrypted withKey:ENCRYPTION_KEY error:&error];
if(error){
NSLog(#"Error:%#", error); //NO ERROR
}
NSLog(#"decrypted: %#", decrypted); //OUTPUT: decrypted:
You're not collecting the data returned by addData:. The engine encrypts/decrypts as you go so that you don't have to hold the entire plaintext and ciphertext in memory. It doesn't accumulate the data unless it has to (for padding reasons). I suspect that the tests that are working are of different lengths than the ones that aren't.
You are correct that using a fixed IV is bad practice. If you use the same IV and key in multiple messages, then it is possible for attackers to recover parts of your messages by comparing the ciphertexts. If you are using AES-CBC without a random IV and an HMAC, then your AES is insecure in several ways. That is the problem RNCryptor was built to address and why the data format looks the way it does.
#jbtule is correct that I didn't particularly mean for people to use the engine directly and haven't heavily documented it, but there's no problem using it, and I can document it better to support that. That said, the engine itself is insanely simple; I just created it as a way to share code between the encryptor and decryptor. There's not much reason to use RNCryptor if you're going to bypass most of the security it provides. For the above code, it'd be a lot simpler to just call the one-shot CCCrypt().

Resources