I've been modifying some code to work between Mac OS X and iPhone OS.
I came across some code that was using NSURL's URLByAppendingPathComponent: (added in 10.6), which as some may know, isn't available in the iPhone SDK.
My solution to make this code work between OS's is to use
NSString *urlString = [myURL absoluteString];
urlString = [urlString stringByAppendingPathComponent:#"helloworld"];
myURL = [NSURL urlWithString:urlString];
The problem with this is that NSString's stringByAppendingPathComponent: seems to remove one of the /'s from the http:// part of the URL.
Is this intended behaviour or a bug?
Edit
Ok, So I was a bit too quick in asking the question above. I re-read the documentation and it does say:
Note that this method only works with file paths (not, for example, string representations of URLs)
However, it doesn't give any pointers in the right direction for what to do if you need to append a path component to a URL on the iPhone...
I could always just do it manually, adding a /if necessary and the extra string, but I was looking to keep it as close to the original Mac OS X code as possible...
I would implement a myURLByAppendingPathComponent: method on NSURL that does the same thing. The reason to give it a different name is so it doesn't override the Apple-provided method when Apple gets around to porting the 10.6 API to the iPhone (so the "my" is just an example — the point is that it's unlikely somebody else would write a method with that name).
It seems to me you just want to mess with the path rather than the whole URL. Here's an untested example:
- (NSURL *)myURLByAppendingPathComponent:(NSString *)component {
NSString *newPath = [[self path] stringByAppendingPathComponent:component];
return [[[NSURL alloc] initWithScheme: [self scheme]
host: [self host]
path: newPath]
autorelease];
}
It would only work correctly with URLs that have file-like paths, but I'm pretty sure the Apple method works the same way. At any rate, hopefully it helps you in the right direction.
URLByAppendingPathComponent
Since iOS 4, URLByAppendingPathComponent is available on iOS and handles the two slashes correctly. (OS X had it since 10.6., as Chuck points out)
myURL = [myURL URLByAppendingPathComponent:#"hello world"]
// http://foo/bar/hello%20world
Note that unlike stringByAppendingPathComponent, this method escapes the argument.
URLWithString:relativeToURL:
Alternatively, there is URLWithString:relativeToURL:, which does not escape. So if the url component is already escaped, use:
myURL = [NSURL URLWithString:#"hello%20world" relativeToURL:myURL]
// http://foo/bar/hello%20world
Note that myURL needs to end with a slash here and the added segment must not have a leading slash.
The NSString Reference says this about stringByAppendingPathComponent:
Note that this method only works with
file paths (not, for example, string
representations of URLs).
So, I think it's a case of "Don't Do That".
Use -stringByAppendingString: instead?
There is a simple work around.
Using [NSString stringWithFormat:#"%#/%#", server, file] works fine.
E.g. server : ftp://www.server.com and file : file.txt
+ (NSString *)urlStringPathWithComponents:(NSArray *)paths
{
NSString *url = #"";
for( NSString *item in paths)
{
if([item isEqualToString:paths.firstObject])
{
url = [url stringByAppendingString:[NSString stringWithFormat:#"%#", item]];
}else{
url = [url stringByAppendingString:[NSString stringWithFormat:#"/%#", item]];
}
}
return url;
}
As you see the code above is a class method and this permit use in anywhere without a instance of an object.
Note: Always the first object of the array must be a base url.
Related
In order to obtain URL, I usually follow this way.
NSString *userText = urlText.text;
NSURL *url = [NSURL URLWithString:userText];
Of coursely, urlText is linked with UITextField.
However, Recently, I saw this code in audioStreaming program.
(This is the program.)
NSString *escapedValue =
[(NSString *)CFURLCreateStringByAddingPercentEscapes(nil, (CFStringRef)downloadSourceField.text, NULL, NULL,
kCFStringEncodingUTF8) autorelease];
NSURL *url = [NSURL URLWithString:escapedValue];
downloadSourceField is linked with UITextField.
What is diffence between these two methodes?
When I replaced the second method (escapedValue = ~~~ ) with (escapedValue = downloadSourceField.text;), the program worked well.
Could you let me know what is difference?
And What is the best method to obtain URL for streaming?
The second method will percent-escape some characters which are typically not allowed in URLs. As an example, the space character is not allowed and will be encoded as %20. NSURL does not support passing a string containing a non-allowed character which has not been escaped to +URLWithString:, therefore passing the string through CFURLCreateStringByAddingPercentEscapes first will let you support such URLs.
interesting,
however, digging in the NSString docs you find these two functions:
- (NSString *)stringByReplacingPercentEscapesUsingEncoding:(NSStringEncoding)encoding
- (NSString *)stringByAddingPercentEscapesUsingEncoding:(NSStringEncoding)encoding
I think those are the "official" way of doing this
Hello everyone
I have an objective-c dilema :P
I am quite new to objective-c, and I have tried searching for an answer, but to no avail.
So, here is my situation.
I will put the code here now, or else it won't make as much sense.
I am putting down what I need, this code does not work right now, and I will get to why later
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setDateFormat:#"MMMM"];
NSString *monthString = [formatter stringFromDate:date];
NSLog(#"MONTH STRING %#", monthString);
NSString *baseURLStr = #"http://www.mysite.ca/example";
NSURL *url = [NSURL URLWithString:[baseURLStr stringByAppendingFormat:#"announcements%20%#%20%d%20carson.ashx", monthString]];
[webView loadRequest:[NSURLRequest requestWithURL:url]];
[NSCalendar release];
So, this is where the problem lies... Encoded (HTML): stringByAppendingFormat:#"announcements%#%20%d%20example.ashx", monthString, day]];
Decoded it looks like this
stringByAppendingFormat:#"announcements %# %d example.ashx"]];
That should be easier to understand..
When I run with the %20's, it says "The requested document was not found"
When I run with spaces (" ") it is just white, and nothing loads.
I know the URL is correct, and if I take the variables out, it is the exact same problem, but when I move the %20's back to the baseURLStr, it works and loads just fine, so it is something to do with the HTML Codes and the "stringbyAppendingFormat" string.
Any help is appreciated!
Thanks for your time
-Kyle
whenever an NSURL returns nil (0x0) after init it is almost always related to in improper url string. And its very picky about getting a properly formatted string.
my recommendation is to simply build your string, without any escapes or url encoding, then simply call
myUrlString = [myUrlString stringByAddingPercentEscapesUsingEncoding:NSASCIIStringEncoding];
here is the header declaration for it
- (NSString *)stringByAddingPercentEscapesUsingEncoding:(NSStringEncoding)enc NS_AVAILABLE(10_3, 2_0);
this way, I always know I get it formatted the way that the NSURL class wants it.
here is an example
NSString *sUrl = #"http://www.myside.ca/example/announcements carson.ashx"; //notice the embedded space
sUrl = [sUrl stringByAddingPercentEscapesUsingEncoding:NSASCIIStringEncoding];
NSURL *url = [NSURL URLWithString:sUrl];
If I understand you correctly, you're trying to generate a string with literal "%20"s embedded in it. The "%" character is special in format strings. If you want to insert a literal percent character, you need to escape it by putting two consecutive "%"s. Like
[NSURL URLWithString:[baseURLStr stringByAppendingFormat:#"foo%%20bar"]];
That would append "foo%20bar" to the end of the string.
You probably just need to encode the percent signs. Try: stringByAppendingFormat:#"announcements%%20%#%%20%d%%20carson.ashx"]
If you're not using printf-style formats, don't use stringByAppendingFormat:. Use stringByAppendingString: instead.
Second, is the resulting URL really supposed to be http://www.myside.ca/exampleannouncements%20%#%20%d%20carson.ashx? Or is there supposed to be a slash in the middle: http://www.myside.ca/example/announcements%20%#%20%d%20carson.ashx?
Also, http://www.myside.ca/exampleannouncements%20%#%20%d%20carson.ashx is an invalid URL. The percent signs that are not part of an escape (e.g. not part of %20) must themselves be encoded, as %25. Technically, the # should also be escaped (as %40), but IIRC NSURL will let that slide.
it may be very easy, but I don't seems to find out why is URLWithString: returning nil here.
//localisationName is a arbitrary string here
NSString* webName = [localisationName stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSString* stringURL = [NSString stringWithFormat:#"http://maps.google.com/maps/geo?q=%#,Montréal,Communauté-Urbaine-de-Montréal,Québec,Canadae&output=csv&oe=utf8&sensor=false&key=", webName];
NSURL* url = [NSURL URLWithString:stringURL];
You need to escape the non-ASCII characters in your hardcoded URL as well:
//localisationName is a arbitrary string here
NSString* webName = [localisationName stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSString* stringURL = [NSString stringWithFormat:#"http://maps.google.com/maps/geo?q=%#,Montréal,Communauté-Urbaine-de-Montréal,Québec,Canadae&output=csv&oe=utf8&sensor=false", webName];
NSString* webStringURL = [stringURL stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSURL* url = [NSURL URLWithString:webStringURL];
You can probably remove the escaping of the localisationName since it will be handled by the escaping of the whole string.
Use This Function if you deal with file saved on file manager.
NSURL *_url = [NSURL fileURLWithPath:path];
I guess you need to use -[NSString stringByAddingPercentEscapesUsingEncoding:]. See Apple doc.
Another comment is that, as an old timer, I find it a bit uneasy to put non-ASCII characters in a source file.
That said, this Apple doc says, starting from 10.4, UTF-16 strings are OK inside #"...".
Somehow GCC seems to correctly convert the source file in Latin-1 into UTF-16 in the binary, but I think it's safest to use
7-bit ASCII characters only inside the source code, and use NSLocalizedString.
I think your accented characters are throwing things off; they won't be handled by -stringByAddingPercentEscapesUsingEncoding:.
NSURL URLWithString:#"" will return nil if the URL does not conform to RFC 2396 and they must be escaped
If you read through rfc2396 in the link you will get loads of details
A great site I found for checking where the offending character is, choose the path option for URL
http://www.websitedev.de/temp/rfc2396-check.html.gz
UPDATE:
Since stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding is deprecated now, you should use stringByAddingPercentEncodingWithAllowedCharacters
[stringURL stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]];
Here's the list of allowed characters set depending on your case.
+ (NSCharacterSet *)URLUserAllowedCharacterSet;
+ (NSCharacterSet *)URLPasswordAllowedCharacterSet;
+ (NSCharacterSet *)URLHostAllowedCharacterSet;
+ (NSCharacterSet *)URLPathAllowedCharacterSet;
+ (NSCharacterSet *)URLQueryAllowedCharacterSet;
+ (NSCharacterSet *)URLFragmentAllowedCharacterSet;
Reference: https://developer.apple.com/reference/foundation/nsstring/1411946-stringbyaddingpercentencodingwit
The URLWithString: call will return a nil if the string passed to it is malformed.
Since NSURL returns nil for malformed urls, NSLog your string and set breakpoints to see exactly what is being passed to your NSURL creation method. If your URLWithString works with a hard coded value, that's further proof that whatever you are passing is malformed.see
You can use NSURL directly without NSString.
//.h file
#interface NewsBrowser : UIViewController {
UIWebView *webView;
NSURL *NewsUrl;
}
#property (nonatomic, retain) IBOutlet UIWebView *webView;
#property(nonatomic,assign)NSURL *NewsUrl;
#end
//.m file
[webView loadRequest:[NSURLRequest requestWithURL:NewsUrl]];
And I pass the URL from another view (using NewsUrl variable) to this view.
Try it.
I am reading string data from a PLIST which I am using to create a JSON string (incidentally for use within Facebook Connect).
NSString *eventLink = [eventDictionary objectForKey:EVENT_FIND_OUT_MORE_KEY];
NSString *eventLinkEscaped = [eventLink stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSString *eventName = [eventDictionary objectForKey:EVENT_NAME_KEY];
NSString *eventDescription = [eventDictionary objectForKey:#"Description"];
NSString *eventImageAddress = [eventDictionary valueForKey:#"Image URL"];
if ([eventImageAddress length] == 0)
{
eventImageAddress = NO_EVENT_IMAGE_URL;
}
// Publish a story to the feed using the feed dialog
FBStreamDialog *facebookStreamDialog = [[[FBStreamDialog alloc] init] autorelease];
facebookStreamDialog.delegate = self;
facebookStreamDialog.userMessagePrompt = #"Publish to Facebook";
facebookStreamDialog.attachment =[NSString stringWithFormat: #"{\"name\":\"%#\",\"href\":\"%#\",\"description\":\"%#\",\"media\":[{\"type\":\"image\",\"src\":\"%#\",\"href\":\"%#\"}]}", eventName, eventLinkEscaped, eventDescription, eventImageAddress, eventLinkEscaped];
[facebookStreamDialog show];
All this works well, but certain event descriptions (4 out of approx. 150) the text that appears in the dialog is blank. I have found the obvious candidates, i.e., the description contains the " character for instance or the copyright symbol. My question is, is there an easy method call, such as stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding that will ensure that any dodgy characters are escaped or ignored?
Thanks in advance,
Dave
I don't think there is an easy way to escape the problem strings. If you need JSON support anywhere else in your code, consider using one of the existing JSON parsing/generator frameworks such as yajl-objc or SBJSON. Either of these will let you build your response as Foundation objects (NSArray/NSDictionary) and then call a single method to generate the appropriate JSON. Your code will be cleaner and you have the benefit that both of these frameworks are well-tested.
If just need to generate this one bit of JSON, your best bet is probably to manually walk over the input strings, replacing potential problem characters with the appropriately escaped versions. Is is not as bad as you might think. Take a look at the source for SBJsonWriter
I'm trying to send the contents of UITextView or UITextField as parameters to a php file
NSString *urlstr = [[NSString alloc] initWithFormat:#"http://server.com/file.php?name=%#&tags=%#&entry=%#",nameField.text, tagsField.text, dreamEntry.text];
When i log urlstr, the url format is ok just as long as the UITextView or UITextField don't contain spaces. How would i go about converting the spaces to %20 ?
edit
here is the code at present, which not only crashes but isn't encoding the url properly.
name=John Doe&tags=recurring nightmare&entry=Testing testing testing
is converted to
name=John -1844684964oe&tags=recurringightmare&entry=Testing 4.214929e-307sting -1.992836e+00sting
- (IBAction)sendButtonPressed:(id)sender
{
NSString *urlString = [[NSString alloc] initWithFormat:#"http://server.com/file.php?name=%#&tags=%#&entry=%#", nameField.text, tagsField.text, dreamEntry.text];
NSString *encodedString = [urlString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSURL *url = [[NSURL alloc] initWithString:encodedString];
NSLog(encodedString);
NSLog(urlString);
[urlString release];
[url release];
[encodedString release];
}
Actually, all of the previous answers contain at least some inaccuracies, which for many common values of user provided text in the TextFields would not correctly communicate with the server
stringByAddingPercentEscapesUsingEncoding: percent escapes all characters which are not valid URL characters. This method should applied once to the entire URL.
A previous answer claims that stringByAddingPercentEscapesUsingEncoding: works like the URL building classes in many scripting languages, where you should not apply it to the entire URL string, but it doesn't. Anyone can easily verify this by checking its output for unescaped &s and ?s. So it is fine to apply to the entire string, but it is not enough to apply to your 'dynamic' url content.
The previous answer is right in that you have to do some more work to the names and values that go into your CGI query string. Since CGI is specified by RFC3875, this is often referred to as RFC3875 percent escaping. It makes sure that your names and values don't contain characters that are valid URL characters but which are significant in other parts of the URL (;, ?, :, #, &, =, $, +, {, }, <, >, and ,)
However, it is very important to also finish by doing plain URL percent escapes on the full string to make sure that all characters in the string are valid URL characters. While you don't in your example, in general there could be characters in a 'static' part of the string which are not valid URL characters, so you do need to escape those as well.
Unfortunately, NSString doesn't give us the power to escape the RFC3875 significant characters so we have to dip down into CFString to do so. Obviously using CFString is a pain so I generally add a Category onto NSString like so:
#interface NSString (RFC3875)
- (NSString *)stringByAddingRFC3875PercentEscapesUsingEncoding:(NSStringEncoding)encoding;
#end
#implementation NSString (RFC3875)
- (NSString *)stringByAddingRFC3875PercentEscapesUsingEncoding:(NSStringEncoding)encoding {
CFStringEncoding cfEncoding = CFStringConvertNSStringEncodingToEncoding(encoding);
NSString *rfcEscaped = (NSString *)CFURLCreateStringByAddingPercentEscapes(
NULL,
(CFStringRef)self,
NULL,
(CFStringRef)#";/?:#&=$+{}<>,",
cfEncoding);
return [rfcEscaped autorelease];
}
#end
With this Category in place, the original problem could be correctly solved with the following:
NSString *urlEscapedBase = [#"http://server.com/file.php" stringByAddingPercentEscapesUsingEncoding:
NSUTF8StringEncoding];
NSString *rfcEscapedName = [nameField.text stringByAddingRFC3875PercentEscapesUsingEncoding:
NSUTF8StringEncoding];
NSString *rfcEscapedTags = [tagsField.text stringByAddingRFC3875PercentEscapesUsingEncoding:
NSUTF8StringEncoding];
NSString *rfcEscapedEntry = [dreamEntry.text stringByAddingRFC3875PercentEscapesUsingEncoding:
NSUTF8StringEncoding];
NSString *urlStr = [NSString stringWithFormat:#"%#?name=%#&tags=%#&entry=%#",
urlEscapedBase,
rfcEscapedName,
rfcEscapedTags,
rfcEscapedEntry];
NSURL *url = [NSURL URLWithString:urlStr];
This is a little variable heavy just be more clear. Also note that the variable list provided to stringWithFormat: should not be nil terminated. The format string describes the precise number of variables that should follow it. Also, technically the strings for query string names (name, tags, entry,..) should be run through stringByAddingPercentEscapesUsingEncoding: as a matter of course but in this small example we can easily see that they contain no invalid URL characters.
To see why the previous solutions are incorrect, imagine that the user input text in dreamEntry.text contains an &, which is not unlikely. With the previous solutions, all text following that character would be lost by the time the server got that text, since the unescaped ampersand would be interpreted by the server as ending the value portion of that query string pair.
You're not supposed to URL-escape the entire string, you're supposed to URL-escape the dynamic components. Try
NSString *urlStr = [NSString stringWithFormat:#"http://server.com/file.php?name=%#&tags=%#&entry=%#",
[nameField.text stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding],
[tagsField.text stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding],
[dreamEntry.text stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding],
nil];
NSURL *url = [NSURL URLWithString:urlStr];
The second issue with your code (and undoubtedly the reason for the odd printing) is you're passing the string directly to NSLog, so it's being treated as a format string. You need to use
NSLog(#"%#", encodedString);
instead. That will make it print as expected.
Edit: A third issue with your code is you're mixing autoreleased and owned objects, then releasing them all at the end. Go look at the 3 objects you create, and which you subsequently release later. One of them shouldn't be released later because it was produced by a method that did not start with the words alloc, copy, or new. Identifying the object in question is an exercise left to the reader.
You can take your URL and use:
NSString *urlStr = [[NSString alloc] initWithFormat:#"http://server.com/file.php?name=%#&tags=%#&entry=%#",nameField.text, tagsField.text, dreamEntry.text];
NSString *encStr = [urlStr stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];