NSURL baseURL returns nil. Any other way to get the actual base URL - objective-c

I think I don't understand the concept of "baseURL". This:
NSLog(#"BASE URL: %# %#", [NSURL URLWithString:#"http://www.google.es"], [[NSURL URLWithString:#"http://www.google.es"] baseURL]);
Prints this:
BASE URL: http://www.google.es (null)
And of course, in the Apple docs I read this:
Return Value
The base URL of the receiver. If the receiver is an absolute URL, returns nil.
I'd like to get from this example URL:
https://www.google.es/search?q=uiviewcontroller&aq=f&oq=uiviewcontroller&sourceid=chrome&ie=UTF-8
This base URL
https://www.google.es
My question is simple. Is there any cleaner way of getting the actual base URL without concatenating the scheme and the hostname? I mean, what's the purpose of base URL then?

-baseURL is a concept purely of NSURL/CFURL rather than URLs in general. If you did this:
[NSURL URLWithString:#"search?q=uiviewcontroller"
relativeToURL:[NSURL URLWithString:#"https://www.google.es/"]];
then baseURL would be https://www.google.es/. In short, baseURL is only populated if the NSURL is created using a method that explicitly passes in a base URL. The main purpose of this feature is to handle relative URL strings such as might be found in the source of a typical web page.
What you're after instead, is to take an arbitrary URL and strip it back to just the host portion. The easiest way I know to do this is a little cunning:
NSURL *aURL = [NSURL URLWithString:#"https://www.google.es/search?q=uiviewcontroller"];
NSURL *hostURL = [[NSURL URLWithString:#"/" relativeToURL:aURL] absoluteURL];
This will give a hostURL of https://www.google.es/
I have such a method published as -[NSURL ks_hostURL] as part of KSFileUtilities (scroll down the readme to find it documented)
If you want purely the host and not anything like scheme/port etc. then -[NSURL host] is your method.

Docs for BaseURL.
baseURL
Returns the base URL of the receiver.
- (NSURL *)baseURL
Return Value
The base URL of the receiver. If the receiver is an absolute URL, returns nil.
Availability
Available in iOS 2.0 and later.
Declared In
NSURL.h
Seems it only works for relative URLs.
You could possibly use ...
NSArray *pathComponents = [url pathComponents]
and then take the bits you want.
Or try...
NSString *host = [url host];

you can use host method
NSURL *url = [[NSURL alloc] initWithString:#"http://www.hello.com"];
NSLog(#"Host:%#", url.host);
result:
Host:www.hello.com

It might be just me, but when I think further about the double-URL solution, it sounds like something that could stop working between OS updates. So I decided to share yet another solution, definitely not beautiful either, but I find it more readable by the general public since it doesn't rely on any hidden specificity of the framework.
if let path = URL(string: resourceURI)?.path {
let baseURL = URL(string: resourceURI.replacingOccurrences(of: path, with: ""))
...
}

Here's a quick, easy, and safe way to fetch the base URL:
NSError *regexError = nil;
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:#"http://.*/" options:NSRegularExpressionCaseInsensitive error:&regexError];
if (regexError) {
NSLog(#"regexError: %#", regexError);
return nil;
}
NSTextCheckingResult *match = [regex firstMatchInString:url.absoluteString options:0 range:NSMakeRange(0, url.absoluteString.length)];
NSString *baseURL = [url.absoluteString substringWithRange:match.range];

Related

confused by substring result of stringByDeletingLastPathComponent

my code
NSMutableString *s= (NSMutableString *)[#"http://www.yahoo.com/index.html" stringByDeletingLastPathComponent];
what I expected result of s is
http://www.yahoo.com
but the code above show s is:
http:/www.yahoo.com
Your comment welcome
You should use NSURL, not NSString:
NSURL *url = [[NSURL URLWithString:#"http://www.yahoo.com/index.html"] URLByDeletingLastPathComponent];
Yielding:
http://www.yahoo.com/
If you absolutely need a string from this, you can then do:
NSString *urlString = url.absoluteString;
Or, if you really needed a mutable string, don’t cast it to NSMutableString, but do create a mutable copy:
NSMutableString *urlString = [url.absoluteString mutableCopy];
But, in general, where possible, you should stay with NSURL when dealing with URLs. And when tempted to use file paths, use file URLs instead.
The annotation for this method is explained as follows。
Apple Document: Note that this method only works with the file paths (not, for example, the string representations of URLs).
But you string is clearly a full URL address, does not belong to the file path, so we will assemble them into a URL, using URL classification URLByDeletingLastPathComponent to intercept
NSString *urlString = #"http://www.yahoo.com/index.html";
NSURL* URL = [NSURL URLWithString: urlString];
NSURL* lastPathUrl = [URL URLByDeletingLastPathComponent];
NSString* lastPathString = lastPathUrl.absoluteString;
NSLog(#"---%#---", lastPathString);

Extract all folders from NSUrl

I have following NSURL, and would need to split it into its various components:
The FTP-Url,
Each of the folders in order to be able to iterate through them.
What is the best way to do this? I know i can use componentsSeparatedByCharactersInSet: but i would like to be sure that it is the BEST way to do it and that there isnt already a function provided to do just that. (like for example to extract the filename from the NSURL)
NSURL *url;
url = [NSURL URLWithString:urlText]; //urlText is ftp.somesite.com/folder1/folder2/folder3
There is a built in way to access it, using the pathComponents property:
NSURL *url = [NSURL URLWithString:#"ftp://ftp.somesite.com/folder1/folder2/folder3"];
NSArray *pathComponents = url.pathComponents;
NSLog(#"%#", pathComponents); // ( #"folder1", #"folder2", #"folder3" )
This is definitely the best approach, since it will handle URL escaping and all that for you.

Regular expression for splitting a URL into its component parts

I'm looking for a way to split a URL into its component parts so I can handle each element. Why? Because the URL is a custom scheme in an iPhone app that I want to handle specifically.
For example, if a user taps a link with the URL myapp://section/?id=123 I want to use the string after the scheme as the indication of which section to jump to in my app and then split the key/value as an indication of which subview to present to the user.
I'm guessing a Regular Expression would do the trick.
Here's an example for the NSURL class and its usage.
NSURL *url = [NSURL URLWithString:#"foo://name.com:8080/12345;param?foo=1&baa=2#fragment"];
NSLog(#"scheme: %#", [url scheme]);
NSLog(#"host: %#", [url host]);
NSLog(#"port: %#", [url port]);
NSLog(#"path: %#", [url path]);
NSLog(#"path components: %#", [url pathComponents]);
NSLog(#"parameterString: %#", [url parameterString]);
NSLog(#"query: %#", [url query]);
NSLog(#"fragment: %#", [url fragment]);
output:
scheme: foo
host: name.com
port: 8080
path: /12345
path components: (
"/",
12345
)
parameterString: param
query: foo=1&baa=2
fragment: fragment
take a look at the docs for NSURL;
http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/NSURL_Class/Reference/Reference.html
scroll down to
Accessing the Parts of the URL
and see all that NSURL can do for you without a regex...
A regular expression is serious overkill for this. Just split first at ://, then at /.
(You could have a loook at using an NSScanner, or as Bill Dudney points out, just use NSURL ;)

How to make QTMovie play file from URL with forced (MP3) type?

I'm using QTKit to progressively download and play an MP3 from a URL. According to this documentation, this is the code I should use to accomplish that:
NSURL *mp3URL = [NSURL URLWithString:#"http://foo.com/bar.mp3"];
NSError *error = nil;
QTMovie *sound = [[QTMovie alloc] initWithURL:mp3URL error:&error];
[sound play];
This works, and does exactly what I want — the MP3 URL is lazily downloaded and starts playing immediately. However, if the URL does not have the ".mp3" path extension, it fails:
NSURL *mp3URL = [NSURL URLWithString:#"http://foo.com/bar"];
NSError *error = nil;
QTMovie *sound = [[QTMovie alloc] initWithURL:mp3URL error:&error];
[sound play];
No error is given, no exception is raised; the duration of the sound is just set to zero, and nothing plays.
The only way I have found to work around this is to force a type by loading the data manually and using a QTDataReference:
NSURL *mp3URL = [NSURL URLWithString:#"http://foo.com/bar"];
NSData *mp3Data = [NSData dataWithContentsOfURL:mp3URL];
QTDataReference *dataReference =
[QTDataReference dataReferenceWithReferenceToData:mp3Data
name:#"bar.mp3"
MIMEType:nil];
NSError *error = nil;
QTMovie *sound = [[QTMovie alloc] initWithDataReference:dataReference error:&error];
[sound play];
However, this forces me to completely download ALL of the MP3 synchronously before I can start playing it, which is obviously undesirable. Is there any way around this?
Thanks.
Edit
Actually, it seems that the path extension has nothing to do with it; the Content-Type is simply not being set in the HTTP header. Even so, the latter code works and the former does not. Anyone know of a way to fix this, without having access to the server?
Edit 2
Anyone? I can't find information about this anywhere, and Google frustratingly now shows this page as the top result for most of my queries...
Two ideas. (The first one being a bit hacky):
To work around the missing content type, you could embed a small Cocoa webserver that supplements the missing header field and route your NSURL over that "proxy".
Some Cocoa http server implementations:
http://code.google.com/p/cocoahttpserver/
http://cocoawithlove.com/2009/07/simple-extensible-http-server-in-cocoa.html
http://culturedcode.com/cocoa/
The second one would be, to switch to a lower level framework (From QTKit to AudioToolbox).
You'd need more code, but there are some very good resources out there on how to stream mp3 using AudioToolbox.
e.g.:
http://cocoawithlove.com/2008/09/streaming-and-playing-live-mp3-stream.html
Personally I'd go with the second option. AudioToolbox isn't as straightforward as QTKit but it offers a clean solution to your problem. It's also available on both - iOS and Mac OS - so you will find plenty of information.
Update:
Did you try to use another initializer? e.g.
+ (id)movieWithAttributes:(NSDictionary *)attributes error:(NSError **)errorPtr
You can insert your URL for the key QTMovieURLAttribute and maybe you can compensate the missing content type by providing other attributes in that dictionary.
This open source project has a QTMovie category that contains methods to accomplish similar things:
http://vidnik.googlecode.com/svn-history/r63/trunk/Source/Categories/QTMovie+Async.m
If you thought weichsel's first solution was hacky, you're going to love this one:
The culprit is the Content-Type header, as you have determined. Had QTKit.framework used Objective-C internally, this would be a trivial matter of overriding -[NSHTTPURLResponse allHeaderFields] with a category of your choosing. However, QTKit.framework (for better or worse) uses Core Foundation (and Core Services) internally. These are both C-based frameworks and there is no elegant way of overriding functions in C.
That said, there is a method, just not a pretty one. Function interposition is even documented by Apple, but seems to be a bit behind the times, compared to the remainder of their documentation.
In essence, you want something along the following lines:
typedef struct interpose_s {
void *new_func;
void *orig_func;
} interpose_t;
CFStringRef myCFHTTPMessageCopyHeaderFieldValue (
CFHTTPMessageRef message,
CFStringRef headerField
);
static const interpose_t interposers[] __attribute__ ((section("__DATA, __interpose"))) = {
{ (void *)myCFHTTPMessageCopyHeaderFieldValue, (void *)CFHTTPMessageCopyHeaderFieldValue }
};
CFStringRef myCFHTTPMessageCopyHeaderFieldValue (
CFHTTPMessageRef message,
CFStringRef headerField
) {
if (CFStringCompare(headerField, CFSTR("Content-Type"), 0) == kCFCompareEqualTo) {
return CFSTR("audio/x-mpeg");
} else {
return CFHTTPMessageCopyHeaderFieldValue(message, headerField);
}
}
You might want to add logic specific to your application in terms of handling the Content-Type field lest your application break in weird and wonderful ways when every HTTP request is determined to be an audio file.
Try replacing http:// with icy://.
Just create an instance like this...
QTMovie *aPlayer = [QTMovie movieWithAttributes:[NSDictionary dictionaryWithObjectsAndKeys:
fileUrl, QTMovieURLAttribute,
[NSNumber numberWithBool:YES], QTMovieOpenForPlaybackAttribute,
/*[NSNumber numberWithBool:YES], QTMovieOpenAsyncOKAttribute,*/
nil] error:error];

NSURL fileURLWithPath where NSString has a space

I've looked at quite a few of the related questions and cannot find a similar problem or a solution so my apologies if there is a duplicate out there somewhere.
Anyway, I'm trying to generate a file's NSURL to use with an NSXMLDocument. I have the following components:
const NSString * PROJECT_DIR = #"~/SP\\ BB/";
const NSString * STRINGS_FILE = #"Localizable.strings";
and construct the URL like so:
NSURL * stringsURL = [NSURL fileURLWithPath:[[NSString stringWithFormat:#"%#%#",PROJECT_DIR,STRINGS_FILE] stringByExpandingTildeInPath]];
however, the resulting path in the NSURL is:
file://localhost/Users/timothyborrowdale/SP2B/Localizable.strings
I have tried changing the PROJECT_DIR to
#"~/SP BB/"
#"~/SP\\\\ BB/" (changes to SP엀2B)
#"~/SP%20BB/"
#"~/SP\%20BB/"
with the same problem. I also tried typing out the file url completely and using [NSURL URLWithString:]
I have also tried using stringByAddingPercentEscapesUsingEncoding with both NSUTF8Encoding and NSASCCIEncoding and these have the same issue.
The NSString displays properly before being passed to NSURL or stringByAddingPercentEscapesUsingEncoding but has the problem once outputted from either.
Try this:
NSString *fnam = [#"Localizable" stringByAppendingPathExtension:#"strings"];
NSArray *parts = [NSArray arrayWithPathComponents:#"~", #"SP BB", fnam, (void *)nil];
NSString *path = [[NSString pathWithComponents:parts] stringByStandardizingPath];
NSURL *furl = [NSURL fileURLWithPath:path];
Foundation has a host of platform-independent, path-related methods. Prefer those over hard-coding path extension separators (often ".") and path component separators (often "/" or "\").
Try abandoning stringWithFormat: (never the right answer for stapling paths together) and stringByExpandingTildeInPath and using NSHomeDirectory() and stringByAppendingPathComponent: instead.
#"~/SP\\ BB/" (changes to SP엀2B)
How did you arrive at that conclusion?