iOS NSURL returning nil on valid URL - objective-c

I've entered the URL http://localhost:8080/a?a=1\\tb?b=2 on the Safari it worked as expected but when using NSURL URLWithString it return nil.
(The server also require \t character)
NSURL *url = [NSURL URLWithString:#"http://localhost:8080/a?a=1\\tb?b=2"];

The problem is that you need to percent-encode your values in the URL string. When it’s received by the server, it will decode this percent-encoded string in the URL into the desired value.
But rather than percent-encoding yourself, you can use NSURLComponents. For example, if you want a to have the value of #"1\\tb", you can do:
NSURLComponents *components = [NSURLComponents componentsWithString:#"http://localhost:8080"];
components.queryItems = #[
[NSURLQueryItem queryItemWithName:#"a" value:#"1\\tb"],
[NSURLQueryItem queryItemWithName:#"b" value:#"2"]
];
NSURL *url = components.URL;
Yielding:
http://localhost:8080?a=1%5Ctb&b=2
Or, if you wanted it to have the tab character in the value associated with a (i.e. %09):
NSURLComponents *components = [NSURLComponents componentsWithString:#"http://localhost:8080"];
components.queryItems = #[
[NSURLQueryItem queryItemWithName:#"a" value:#"1\tb"],
[NSURLQueryItem queryItemWithName:#"b" value:#"2"]
];
NSURL *url = components.URL;
Yielding:
http://localhost:8080?a=1%09b&b=2
It just depends upon whether your server is expecting two characters, the \ followed by t (the first example) or the single \t character (the second example). Either way, the respective use of NSURLComponents will take care of the percent-encoding for you, and your server will decode it.
For what it’s worth, the one caveat is the + character, which NSURLComponents won’t percent-encode for you (because, technically, a + character is allowed in a URL query). The problem is that the + character is interpreted as a space character by most web servers (per the x-www-form-urlencoded spec). If you need to pass a literal + character, you might want to replace those + characters, as advised by Apple:
NSURLComponents *components = [NSURLComponents componentsWithString:#"http://localhost:8080"];
components.queryItems = #[
[NSURLQueryItem queryItemWithName:#"q" value:#"Romeo+Juliet"]
];
components.percentEncodedQuery = [components.percentEncodedQuery stringByReplacingOccurrencesOfString:#"+" withString:#"%2B"];
NSURL *url = components.URL;

Related

Objective-C URL string contains single percent sign

I have a string which ends with percent sign(%),
this string is prepared for an URL request as a parameter:
NSString *parameter = #"/param=%";
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL urlWithString:[NSString stringWithFormat:#"http://www.whatev%#",parameter]]];
The request returns nil.
I've tried:
NSString *parameter = #"/param=\uFF05";
//request returns nil
and
NSString *parameter = #"/param=%";
NSString *newParameter = [parameter stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
//request returns /param=%25 ...where does 25 come from?!
How could I have only one % converted to a request url?
Any advice would be appreciated.
The percent sign has a special purpose in URL's and is used to encode special characters of all kinds. For example a space ( ) is %20 and the percent sign itself is %25.
http://en.wikipedia.org/wiki/Percent-encoding
The last one should be correct, I assume you have problems using it as a request?
escape sequence for % is %%, so #"/param=%%" should solve the problem

Objective-C Base64 decoding returns nil

I have downloaded and added the following category into my project: https://github.com/nicklockwood/Base64
NSString *secret = #"7pgj8Dm6";
NSString *decodedSecret = [secret base64DecodedString];
NSLog(#"decoded Secret = %#", decodedSecret);
This however, always turns out to be nil. What is happening here ?
I hope it will helpful to you
NSString *decodeString = #"Raja";
Encode String
NSData *encodeData = [decodeString dataUsingEncoding:NSUTF8StringEncoding];
NSString *base64String = [encodeData base64EncodedStringWithOptions:0];
DLog(#"Encode String Value: %#", base64String);
Decode String
NSData *decodedData = [[NSData alloc] initWithBase64EncodedString:base64String options:0];
NSString *decodedString = [[NSString alloc] initWithData:decodedData encoding:NSUTF8StringEncoding];
DLog(#"Decode String Value: %#", base64String);
Thanks
I've failed to relax iOS native base64 decoder enough with NSDataBase64DecodingIgnoreUnknownCharacters to process jwt payloads
so I had to revert to a 3rdparty MF_Base64Codec
Sadly you might want to consider doing the 3rdparty route
until Apple will have fixed the bug or relaxed the decoder further to
accept what it currently considers crap input
Your input 7pgj8Dm6is an Invalid base64 encoded string. So the result will be always nil since the method base64DecodedString used only for UTF8 encoding.
As of iOS 7 and Mac OS 10.9, this library is not longer needed.
You can use initWithBase64EncodedString:options:
The default implementation of this method will reject non-alphabet characters, including line break characters. To support different encodings and ignore non-alphabet characters, specify an options value of NSDataBase64DecodingIgnoreUnknownCharacters.

in objective-c is there any easy way to add backslash in front of special characters?

Note: Not sure why this is marked as duplicate as I clearly stated that I don't want to use stringByReplacingOccurrencesOfString over and over again.
I have a question regarding the special character filename.
I have implemented a program, so that when you open a file or multiple files, the program will read all these filenames and local path and store them into the NSMutableArray. This part works perfectly without a problem.
My program also need to use NSTask to manipulate these files. However, the problem is, sometimes filename will contain special characters, for example, /Users/josh/Desktop/Screen Shot 2013-03-19 at 2.05.06 PM.png.
I have to replace space with backslash and space
NSString *urlPath = [[self url] path];
urlPath = [urlPath stringByReplacingOccurrencesOfString:#"(" withString:#"\\("];
urlPath = [urlPath stringByReplacingOccurrencesOfString:#")" withString:#"\\)"];
urlPath = [urlPath stringByReplacingOccurrencesOfString:#" " withString:#"\\ "];
to: /Users/josh/Desktop/Screen\ Shot\ 2013-03-19\ at\ 2.05.06\ PM.png
so that I can manipulate the file properly.
Same for the ( and ). I also need to add backslash before that.
but there are too many special characters. ie.
/Users/josh/Desktop/~!##$?:<,.>%^&*()_+`-={}[]\|'';.txt
I need to change to:
/Users/josh/Desktop/\~\!#\#\$\?\:\<\,.\>\%^\&\*\(\)_+\`-\=\{\}\[\]\\\|\'\'\;.txt
and not to mention other special characters (ie. accent)
Is there any easy way to put a backslash in front of each special character, as I don't want to keep calling stringByReplacingOccurrencesOfString over and over again.
As described in NSTask's documentation for the setArguments: method, there should be no need to do special quoting:
Discussion
The NSTask object converts both path and the strings in
arguments to appropriate C-style strings (using
fileSystemRepresentation) before passing them to the task via argv[].
The strings in arguments do not undergo shell expansion, so you do not
need to do special quoting, and shell variables, such as $PWD, are not
resolved.
If you feel it is necessary, can you please provide some examples of the commands you want to run in the NSTask?
[UPDATE]: I see in the comments that you indeed are using the NSTask to execute a bash shell with -c, which I had wondered about. I've generally used NSTask to execute the command directly rather than going through the shell, like this:
NSTask *task = [[NSTask alloc] init];
[task setLaunchPath:#"/bin/ls"];
[task setArguments:[NSArray arrayWithObjects:#"-l", self.url.path, nil]];
Can you give a more accurate example of the actual command you want to run? For example, are you piping a series of commands together? Perhaps there might be an alternate way to achieve the same results without the need for using the bash shell...
I think you may be able to use an NSRegularExpressionSearch search.
It would look something like this
+ (NSString *) addBackslashes: (NSString *) string
{
// First convert the name string to a pure ASCII string
NSData *asciiData = [string dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES];
NSString *asciiString = [[[NSString alloc] initWithData:asciiData encoding:NSASCIIStringEncoding] lowercaseString];
// Define the characters that we will replace
NSString *searchCharacters = #"PUT IN ALL OF YOUR SPECIAL CHARACTERS HERE";
// example NSString *searchCharacters = #"!##$%&*()";
// replace them
NSString *regExPattern = [NSString stringWithFormat:#"[%#]", searchCharacters];
string = [asciiString stringByReplacingOccurrencesOfString:regExPattern withString: [NSString stringWithFormat:#"\\%#", regExPattern] options:NSRegularExpressionSearch range:NSMakeRange(0, asciiString.length)];
return string;
}
you could maintain a set of strings that need to be escaped and use NSScanner to build the new string by iterating the the source string and each time a problematic character is found u first add \\ to a destination string and continue coping the next chars.
NSString *sourceString = #"/Users/josh/Desktop/\"Screen Shot\" 2013-03-19 at 2\\05\\06 PM.png";
NSMutableString *destString = [#"" mutableCopy];
NSCharacterSet *escapeCharsSet = [NSCharacterSet characterSetWithCharactersInString:#" ()\\"];
NSScanner *scanner = [NSScanner scannerWithString:sourceString];
while (![scanner isAtEnd]) {
NSString *tempString;
[scanner scanUpToCharactersFromSet:escapeCharsSet intoString:&tempString];
if([scanner isAtEnd]){
[destString appendString:tempString];
}
else {
[destString appendFormat:#"%#\\%#", tempString, [sourceString substringWithRange:NSMakeRange([scanner scanLocation], 1)]];
[scanner setScanLocation:[scanner scanLocation]+1];
}
}
NSLog(#"\n%#\n%#", sourceString, destString);
result:
/Users/josh/Desktop/Screen Shot 2013-03-19 at 2.05.06 PM.png
/Users/josh/Desktop/Screen\ Shot\ 2013-03-19\ at\ 2.05.06\ PM.png

-[NSString stringByAppendingPathComponent:] or just -[NSString stringByAppendingFormat:] for NSStrings for NSURLs?

When calling +[NSURL URLWithString:] I have two options for building my URLs:
[[#"http://example.com" stringByAppendingPathComponent:#"foo"] stringByAppendingPathComponent:#"bar"]
or
[#"http://example.com" stringByAppendingFormat:#"/%#/%#",#"foo",#"bar"];
-[NSString stringByAppendingPathComponent:] seems like the more correct answer, but do I lose anything using -[NSString stringByAppendingFormat:] besides handling double-slashes as in the following case?
// http://example.com/foo/bar
[[#"http://example.com/" stringByAppendingPathComponent:#"/foo"] stringByAppendingPathComponent:#"bar"]
// http://example.com//foo/bar oops!
[#"http://example.com/" stringByAppendingFormat:#"/%#/%#",#"foo",#"bar"];
As you're working with URLS, you should use the NSURL methods:
NSURL * url = [NSURL URLWithString: #"http://example.com"];
url = [[url URLByAppendingPathComponent:#"foo"] URLByAppendingPathComponent:#"bar"]
or in Swift
var url = NSURL.URLWithString("http://example.com")
url = url.URLByAppendingPathComponent("foo").URLByAppendingPathComponent(".bar")
I just ran into a problem with stringByAppendingPathComponent: it removes double slashes everywhere!:
NSString* string1 = [[self baseURL] stringByAppendingString:partial];
NSString* string2 = [[self baseURL] stringByAppendingPathComponent:partial];
NSLog(#"string1 is %s", [string1 UTF8String]);
NSLog(#"string2 is %s", [string2 UTF8String]);
for a baseURl of https://blah.com
and a partial of /moreblah
Produces the two strings:
2012-09-07 14:02:09.724 myapp string1 is https://blah.com/moreblah
2012-09-07 14:02:09.749 myapp string2 is https:/blah.com/moreblah
But for some reason my calls to blah.com to get resource work with the single slash. But it indicates to me that stringByAppendingPathComponent is for paths - NOT urls.
This is on iPhone 4 hardware running iOS 5.1.
I outputted the UTF8 strings as I wanted to make sure that the debugger output I was seeing was believable.
So I guess I am saying - don't use path stuff on URLs, use some home brew or a library.
How about:
[NSString pathWithComponents:#[#"http://example.com", #"foo", #"bar"]]
As pointed out in the comments a / gets stripped from protocol when using the methods from NSPathUtitlites.h, so that is the obvious downfall. The solution I could come up with that is closest to the original one I posted is:
[#[ #"http://example.com", #"foo", #"bar" ] componentsJoinedByString:#"/"]
You will just need to use a literal for the path separator which is what NSString does.
NSString represents paths generically with ‘/’ as the path separator
and ‘.’ as the extension separator.
the point of stringByAppendingPathComponent is to handle double slashes, however, you could do something like:
[[#"http://example.com/" stringByAppendingPathComponent:[NSString stringWithFormat:#"%#/%#", #"foo", #"bar"]]

How to remove certain sets of punctuation bet retain others in a string?

I'm creating a URL from various parts in order to make a phone call using
NSURL* url = [NSURL URLWithString:[kCallURLBase stringByAppendingString:numberStr]];
[[UIApplication sharedApplication] openURL:url];
// kCallURLBase is "tel:"
If numberStr contains '(' or ')' then url is null, and from reading other postings on here people have been having difficulty if the number contains spaces or '-' etc. So I added the following:
NSMutableCharacterSet *charSet = [[NSMutableCharacterSet alloc] init];
[charSet formUnionWithCharacterSet:[NSCharacterSet whitespaceCharacterSet]];
[charSet formUnionWithCharacterSet:[NSCharacterSet punctuationCharacterSet]];
[charSet formUnionWithCharacterSet:[NSCharacterSet symbolCharacterSet]];
NSArray *arrayWithNumbers = [self.number componentsSeparatedByCharactersInSet:charSet];
NSString *numberStr = [arrayWithNumbers componentsJoinedByString:#""];
However some numbers might be of the form *56 as they are being made from a handset in which case the * character needs to be retained. How can I remove all the other unnecessary characters but retain the *?
Alternatively, is there a better solution then this approach?
Create a character set using the characters you do want (digits, #, and *), then split your input string on any characters that are not in that set, and join the results back together. That will leave you with only valid characters in your string.
NSString *numberStr = #"(212) 555-1212 *99";
NSCharacterSet *illegalCharSet = [[NSCharacterSet characterSetWithCharactersInString:#"1234567890*#"] invertedSet];
NSString *convertedStr = [[numberStr componentsSeparatedByCharactersInSet:illegalCharSet] componentsJoinedByString:#""];
// convertedStr => 2125551212*99
Actually, I think that instead of filtering out disallowed characters, it'd be better to just take the good ones. Those would basically be: digits, *, #. So you could just iterate through the string, and collect characters representing digits, *, # in an array, and then join components of this array.
Probably the easy way to iterate over the string is to use something like:
const char *the_string = [numberString UTF8String];
Then just iterate from the_string[0] to the_string[ strlen(the_string) - 1 ].
And you could fairly easy test for digits either using isdigit(the_string[i]) from ctype.h, or doing if (the_string[i] >= '0' && the_string[i] <= '9').