Using format specifier in for a URL in iOS - ios

I'm having problems with using format specifiers for url's. Using the zBar scanner to scan a php link. I've spent a lot of time researching here on overflow but I cannot find the answer.
This is the code I use in my viewcontroller.m:
NSData *jsonData = [NSData dataWithContentsOfURL:[NSURL URLWithString:[NSString stringWithFormat:#"http://mywebsite.co.uk/TM.php?id=%#",self.scannedValue]]];
//NSData *jsonData = [NSData dataWithContentsOfFile:[[NSBundle mainBundle] pathForResource:#"Sample" ofType:#"JSON"]];
NSArray *jsonObject = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableLeaves error:Nil];
I'm trying to get the part after id to take in any integer number like 200,201,202,203,...
For some strange reason it only works when I type the number in itself as id=201 or id=202.
when I use NSLog for NSLog(#"%#",scannedValue) it shows the following:
2014-02-26 21:45:27.185 MeCombine[2412:60b] http://mywebsite.co.uk/TM.php?id=201
2014-02-26 21:45:27.277 MeCombine[2412:60b] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'data parameter is nil'
Can anyone tell me why this happens please?
A few notes:
scannedValue = sym.data;
is string, sum is ZBarSymbol class object
In the ZBarSymbol class
#property (readonly, nonatomic) NSString *data;
is the starting value.
When a value is successfully scanned it will return a NSString *data in the delegate method.

I can only provide a guess, since I don't see all of your code, but here's what I think goes on:
Your http link is malformed (http:: instead of http:). The jsonData object will be nil if it can't load the URL for whatever reason.
You don't do any null checking, and this nil object is sent to whatever method processes your json data, which throws a NSInvalidArgumentException if the data object is nil.

NSData's +dataWithContentsOfURL: does not tolerate nil URL, which in this case happens because of the incorrect URL format, notice you have two : in the URL after http. The correct URL would be.
http://mywebsite.co.uk/TM.php?id=%#
The modern and potentially more foolproof way of building URLs would be to use NSURLComponents (iOS 7 and OS X 10.9)
NSURLComponents *comps = [NSURLComponents componentsWithString:#"http://www.mywebsite.co.uk"];
comps.query = [NSString stringWithFormat:#"id=%#", self.scannedValue];
comps.path = #"/TM.php";
NSURL *URL = comps.URL;
The main benefit would be the ability to isolate the base URL into a constant string and then alter the query/path from elsewhere, both making things a bit more modular and easier to maintain than a hardcoded value in code.

Related

How do I urlencode this NSString that contains a JSON inside it?

I have the following json string that should be sent to the backend
{
id = "MU_200255802";
keywords = (
Talk,
games,
meetup,
time,
meet,
"Time for Another Game"
);
}
So before this JSON I have the java servlet URL something like
http://....net/servletName?
How should I urlencode the json string and the url because even after trying several options, I keep getting bad url as an error back in the delegate method. What is the right way to do it/
I tried encoding using
NSString *urlStringEncoded = [[NSString stringWithString:urlString] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
and also used other encoding formats too.
Use this to create json string
NSError *error;
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:#"Your object" options:NSJSONWritingPrettyPrinted error:&error];
NSString *jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
You can use Google Toolbox for Mac, check it out here, https://code.google.com/p/google-toolbox-for-mac/source/checkout
There are Classes named GTMNSString+HTML,GTMNSString+XML,GTMNSString+URLArguments, which contains many encoding methods for you.

How to use stringWithContentsOfFile:usedEncoding:error:

I've tried to load a file with unknown encoding. This is because I dont always have control over the file that I will load. I assumed that the method stringWithContentsOfFile:usedEncoding:error: will do this and will let me know the file encoding. Unfortunately following code doesn't provide the encoding I want - it always return 0.
NSStringEncoding *encoding = nil;
NSError *error = nil;
NSString *json = [NSString stringWithContentsOfFile:path
usedEncoding:encoding
error:&error];
NSLog(#"\n%lu\n%#",(unsigned long)encoding,error);
It returns content of file, so you may wonder why I need this encoding, well that string is JSON that I want to serialize it into NSDictionary and the dataUsingEncoding: method requires encoding. I tried to pass encoding variable but this throws an error. So I tried fail safe UTF8 encoding and then it worked.
NSData *jsonData = [json dataUsingEncoding:NSUTF8StringEncoding];
So I must using this incorrect as encoding equals to 0 instead of 4 (UTF8). Can someone help me with that?
Try that :
NSStringEncoding encoding;
NSError *error = nil;
NSString *json = [NSString stringWithContentsOfFile:path
usedEncoding:&encoding
error:&error];
NSLog(#"\n%lu\n%#",(unsigned long)encoding,error);
To be clearer, you can't receive the encoding value in your pointer, you need to give a plain NSStringEncoding address
Since you are not aware of the encoding of the file, I will suggest you to see this link.
Its basically String Programming Guide which will let you know in depth what to do.
Below is the snapshot for which you are looking into:
Hope this will help you. Happy coding :)

NSJSONSerialization in my function returns an error

I have a function that looks like this:
-(void)writeArrayOnFile {
NSArray* converterArray = [[NSArray alloc] initWithArray:[self swContainer]];
NSURL *url = [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
url = [url URLByAppendingPathComponent:#"saved.json"];
NSError *e = nil;
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:converterArray options:0 error:&e];
if (jsonData) {
[jsonData writeToFile:url.path atomically:YES];
}
NSLog(#"%#", jsonData);
}
On breakpoint, the converterArray comes in with correct objects:
[0] Movie * 0x8d5e100 0x08d5e100
NSObject NSObject
_swBtnValue BOOL YES
_thSmallLink UIImage * 0x8fdcd80 0x08fdcd80
_thLargeLink __NSCFString * #"http://content9.flixster.com/movie/11/17/45/11174563_ori.jpg" 0x08fb9530
_mvName __NSCFString * #"Delivery Man" 0x08fc48e0
_dvdReleaseDate __NSCFString * #"2014-03-25" 0x08fb49a0
_mvRating __NSCFString * #"PG-13" 0x08fc4920
_mvSyn __NSCFString * #"From DreamWorks Pictures comes "Delivery Man", the story of affable underachiever David Wozniak, whose mundane life is turned upside down when he finds out that he fathered 533 children through sperm donations he made twenty years earlier. In debt to the mob, rejected by his pregnant girlfriend, things couldn't look worse for David when he is hit with a lawsuit from 142 of the 533 twenty-somethings who want to know the identity of the donor. As David struggles to decide whether or not he should reveal his true identity, he embarks on a journey that leads him to discover not only his true self but the father he could become as well. (c) Disney" 0x08fadd00
[1] Movie * 0x8fda800 0x08fda800
[2] Movie * 0x8d68720 0x08d68720
but on breakpoint the "writeToFile" method (from the jsonData object) returns this: Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Invalid type in JSON write (Movie)
Is it because of the UIImage value? What am I not seeing? Thanks much.
Yes, it's because of the UIImage. NSJSONSerialization only works with NSString, NSNumber, NSArray, NSDictionary, and NSNull. See the NSJSONSerialization docs for full details.
If you want to include the image, you'll have to convert it to NSData first, probably using either UIImagePNGRepresentation or UIImageJPEGRepresentation.

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().

NSJSONSerialization from NSString

Is it possible if I have a NSString and I want to use NSJSONSerialization? How do I do this?
First you will need to convert your NSString to NSData by doing the following
NSData *data = [stringData dataUsingEncoding:NSUTF8StringEncoding];
then simply use the JSONObjectWithData method to convert it to JSON
id json = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
You need to convert your NSString to NSData, at that point you can use the +[NSJSONSerialization JSONObjectWithData:options:error:] method.
NSString * jsonString = YOUR_STRING;
NSData * data = [jsonString dataUsingEncoding:NSUTF8StringEncoding];
NSError * error = nil;
id json = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error];
if (!json) {
// handle error
}
You can convert your string to NSData by saying:
NSData *data = [string dataUsingEncoding:NSUTF8StringEncoding];
You can then use it with NSJSONSerialization. Note however that NSJSONSerialization is iOS5 only, so you might be better off using a library like TouchJSON or JSONKit, both of which let you work directly with strings anyway, saving you the step of converting to NSData.
I wrote a blog post that demonstrates how to wrap the native iOS JSON class in a general protocol together with an implementation that use the native iOS JSON class.
This approach makes it a lot easier to use the native functionality and reduces the amount of code you have to write. Furthermore, it makes it a lot easier to switch out the native implementation with, say, JSONKit, if the native one would prove to be insufficient.
http://danielsaidi.com/blog/2012/07/04/json-in-ios
The blog post contains all the code you need. Just copy / paste :)
Hope it helps!

Resources