Detect URL valid for iOS - objective-c

I want to detect if the URL is valid. So I see the following function.
+ (id)URLWithString:(NSString *)URLString
As the Apple Documentation said, an NSURL object initialized with URLString. If the string was malformed, returns nil. But actually, I call URLWithString with any string, it will return not nil. This documentation is wrong?
And could you provide a workaround to detect if URL is valid?

The documentation isn't wrong - NSURL supports both absolute and relative URLs. Additionally, it's not just used to remote (web) URLs, but for local file URLs as well. For example:
// Totally valid NSURL
NSURL *url = [NSURL URLWithString:#"http://google.com"];
// Just as valid NSURL
NSURL *url2 = [NSURL URLWithString:#"2rwehrfuiw34twef"];
You don't get any guarantees that the URL is reachable. It does guarantee you URL complies with RFC 2396. If you want to check that a URL is available you can either use the checkResourceIsReachableAndReturnError: method (which is available from iOS 5 onwards, but should only be used for file path URLs) or make a network request for it (which you'll have to do if your URL isn't a file path).

Related

NSString's stringWithContentsOfURL uses browser cookies?

I'm working on an objective-c project that downloads webpages from a community website and parses the results. The download code looks like this:
NSError* error = nil;
NSString* text = [NSString stringWithContentsOfURL:fileUrl encoding:NSASCIIStringEncoding error:&error];
if(text) {
return text;
}
else {
NSLog(#"Error = %#", error);
return nil;
}
The odd thing is that when I download from the site I see resulting content that I would only see if logged into the site (which, in my browser, I am).
Does that method (NSString stringWithContentsOfURL:encoding:error) use browser cookies when executing the request? If so, is it Safari specifically that it's integrated with? The default browser? I can't seem to find documentation describing the behavior that I'm seeing. I'm ok with the behavior (in fact, it's preferable), but I only want to depend on it if I fully understand what's going on.
Thanks for your time.
Cookies are automatically handled and stored in an app's NSHTTPCookieStorage shared instance. Call the cookies method and check to see if your cookie is there. If it is, then that confirms your suspicion.
EDIT: I highly suspect you are using a UIWebView in your app and logging in from there. In that case, then yes, cookies are stored in your app's NSHTTPCookieStorage shared instance and will be used with further URL requests.

Objective C , opening a website without opening it in Safari (for server call purposes)

I am using objective C and I am trying to load a website without opening it in Safari :
I am using the following function :
NSString *website =[NSString stringWithFormat:#"http://www.website.com"];
NSString *connected = [NSString stringWithContentsOfURL:[NSURL URLWithString:website ]encoding:NSStringEncodingConversionAllowLossy error:nil];
but when I check in my database I noticed that it does not load the website , when I load it in the browser it does detect it tho , any reasons ?
thanks
You probably want to use the NSURLConnection class to retrieve data from the network. It will provide a much more useful set of errors, etc.
For simple (but blocking), you can start off with -sendSynchronousRequest:returningResponse:error:, which retrieves a URL from the network giving you the data (in the return value), the HTTP Response, and an error.
NSURL *url = [NSURL URLWithString: #"http://www.website.com"];
NSURLRequest *request = [NSURLRequest requestWithURL: url];
NSURLResponse *response;
NSError *error;
NSData *data = [NSURLConnection sendSynchronousRequest: request returningResponse: &response error: &error];
If data isn't nil, you've gotten a valid response, otherwise error contains information about the response. You still need to double-check the response in order to make sure you got what you wanted (not a "successful" response that resulted in an error due to the server sending back an error code for HTTP).
In the future, if you need to do a POST command, you can use NSMutableURLRequest and set the type of the request.
Vlad is right, but your code will also only load one file. When you open that file in a browser like Safari, the browser will recursively load all referenced files and will handle any redirect that is present.
UPDATE
In a browser, using the URL without the file name works. I'm not sure you can do that with stringWithContentsOfURL: or with an NSURLConnection.
If the file name is something like "index.html" or whatever it is, try adding that to your URL. These methods load files, so I am betting that you need to provide a fully defined URL, including the file name.

ASIHTTPRequest seems to cache JSON data always

I'm using ASIHTTPRequest API to get some JSON data from a web side. I'm using an asynchronous request without changing default cache properties. Code is as follows:
__unsafe_unretained ASIHTTPRequest * request = [ASIHTTPRequest requestWithURL:url];
request.timeOutSeconds = 30;
[request setCompletionBlock:^(void)
{
NSString *str = [request responseString];
/*
*
*
*/
}];
[request setFailedBlock:^(void)
{
NSLog(#"status: %#",[request responseStatusMessage]);
NSLog(#"%# : %#",[request url],[[request error] debugDescription]);
}];
[request startAsynchronous];
However, obtained JSON data remains as the old one although content of JSON data in server changes.
I checked data using web browser, both on laptop and on iPhone safari. When i request url after a change in JSON, it first returns old data, but if i refresh the page, it returns updated data. But in the app, ASIHTTPRequest always returns the old data.
I also try to debug ASIHTTPRequest code in order to see whether any cached data used. But it seems like it never uses download cache because it has not been set. It never enters [useDataFromCache] method.
What could be the problem? How can i force ASIHTTPRequest to check whether there is an updated data on server, and make it get the true updated JSON?
EDIT
I used Cache-Control header, and now i get the correct updated JSON data. Code is as follows:
[request addRequestHeader:#"Cache-Control" value:#"no-cache"];
However, i think from now on request will always try to retrieve JSON even if it is not modified, which will decrease performance.
How can i make it first check the server whether data is modified, and retrieve if it is modified? Currently i get JSON data as a dynamic response from a php url, so there is no file which i can check up to dateness of the data.
What could be the solution?
Regards,
Given everything you've said, it seems unlikely that ASIHTTPRequest is cacheing the response.
So, something else must be - it seems like you have a cacheing proxy server inbetween you and the server, and that's why setting Cache-Control makes a difference.
It could be a proxy server on your local network, or it could be at your ISP, or it could be in front of the web server you're using.
According to ASIHTTPRequest's documentation, calling the method
[[ASIDownloadCache sharedCache] clearCachedResponsesForStoragePolicy:ASICachePermanentlyCacheStoragePolicy];
will clear the cache. Call this method before you send the request and it should give you the updated JSON data.

Cocoa HTTP PUT with content-range

Is it possible to use an NSURLConnection/NSURLRequest combination to send a PUT request to a server with a Content-Range header? By that I mean I want to resume an upload to the server which can accept a byte range in the request to resume the upload.
I see you can set an NSInputStream as the request body so I figured that I could subclass that and override the the open/seek functions and set the request header but it seems to call on undocumented selectors and break the implementation.
I'm sure I could do this with CFNetwork but it just seems like there must be a way to do it with the higher level APIs.
Any ideas on where to start?
EDIT:
To answer my own question, this is indeed possible after reading a blog [http://bjhomer.blogspot.com/2011/04/subclassing-nsinputstream.html] which details the undocumented callbacks which relate to CFStream. Once those are implemented I can call the following in the open callback to skip ahead:
CFReadStreamSetProperty((CFReadStreamRef)parentStream, kCFStreamPropertyFileCurrentOffset, (CFNumberRef)[NSNumber numberWithUnsignedLongLong:streamOffset]);
Thanks,
J
I think the server needs to supports put method combines with range but this will be the way to do it with high level Objective-C API
NSURL *URL = [NSURL URLWithString:strURL];
NSMutableURLRequest *urlRequest = [NSMutableURLRequest requestWithURL:URL];
NSString *range = [NSString stringWithFormat:#"bytes=%lld-%lld",bytesUploaded,uploadSize];
[urlRequest addValue:range forHTTPHeaderField:#"Range"];
[urlRequest setHTTPMethod:#"PUT"];
self.connection = [NSURLConnection connectionWithRequest:urlRequest delegate:self];
Cheers
First, if you want to do fancy work with HTTP, I typically recommend ASIHTTPRequest. It's solid stuff that simplifies a lot of more complicated HTTP problems. It's not really needed for setting a simple header, but if you're starting to build something more complex, it can be nice to move over to ASI sooner rather than later.
With an NSMutableURLRequest, you can set any header you want using addValue:forHTTPHeaderField:. You can use that to set your Content-Range.
Like I posted in my comment, you can facilitate what you want without dropping down to the CoreFoundation level:
As NSInputStream inherits NSStream, it is possible to prepare the stream as follows:
NSNumber *streamOffset = [NSNumber numberWithUnsignedInteger:lastOffset];
[inputStream setProperty:streamOffset forKey: NSStreamFileCurrentOffsetKey];
(Assuming lastOffset is an NSUInteger representation of your last file offset in bytes and inputStream is the stream you want to set as the request's HTTPBodyStream.)

Reading XML File from Server

I'm currently parsing an XML file that resides in my bundle using NSXMLParser with the following line:
NSURL *xmlURL = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:#"XMLFileName" ofType:#"xml"]];
But I want to put the same file on my server instead. I can't find an example of how to call the same file from my server. Any help is appreciated.
lq
NSURL has several methods for creating URLs, one of which is -URLWithString:; like so:
NSURL *xmlURL = [NSURL URLWithString:#"http://example.com/example.xml"];
That URL can be passed directly to NSXMLParser; but you might want to do so in a secondary thread.
If you want to HTTP GET the file from your server then you should look at NSURLConnection. It allows you to do synchronous or asynchronous HTTP requests.