I've written a short iOS application that contacts a server via the usual:
NSString *URLString = #"http://mysite/script.php?foo=1";
NSURL *getURL = [NSURL URLWithString:URLString];
NSURLRequest *getRequest = [NSURLRequest requestWithURL:getURL];
NSURLResponse *response = nil;
NSError *error = nil;
NSData *responseData = [NSURLConnection sendSynchronousRequest:getRequest returningResponse:&response error:&error];
This works OK, but as I access the web site relatively frequently with various values of foo, and as we plan to migrate to https, people have raised the question about the overhead on the phone of setting up and tearing down the NSURLConnection every time. I see this answer saying that the class handles caching, but is that true for https too?
According to my tests, yes, it does appear to be true that the NSURLConnection class does caching of https to some extent. I did like the look of AFHTTPClient as mentioned in the comment, but due to various issues I was unable to use it in this project, however.
Related
I'm trying to connect to a web service that if I hit it through Safari I get back some json, but when I hit it through code I get a NSURLErrorDomain error with the following description.
"The certificate for this server is invalid. You might be connecting to a server that is pretending to be "url" which could..."
Here is the code:
NSURL *url = [NSURL URLWithString:#"webservice_url"];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
[request setHTTPMethod:#"GET"];
NSError *error = nil;
NSHTTPURLResponse *responseCode = nil;
NSData *oResponseData = [NSURLConnection sendSynchronousRequest:request returningResponse:&responseCode error:&error];
Can anyone explain how to get around this? Also, it is a https web service.
The website doesn't have a HTTPS certificate. Try looking into this:
https://stackoverflow.com/a/15011030/4716039
In your code, the URLWithString value needs to be an actual URL. It is the same thing as going to Safari and typing in "webservice_url" as the entire URL instead of giving it the entire address. Instead you'll want to do something like the following:
NSURL *url = [NSURL URLWithString:#"https://www.urlgoeshere.com/getJSON"];
You can use like this -
NSURL *baseURL = [NSURL URLWithString:#"http://example.com/v1/"];
[NSURL URLWithString:#"foo" relativeToURL:baseURL];
// http://example.com/v1/foo
I'm using this line to get the contents of a URL:
NSString *result=[[NSString alloc] initWithContentsOfURL:[NSURL URLWithString:URL]
encoding:NSUTF8StringEncoding
error:nil];
The problem is when there's a bad connection, it loads the contents from cache. Is there a way to avoid this behaviour? For example, clearing the cache or something.
First, it's not recommended to use initWithContentsOfURL:encoding:error to load data from a network resource.
Second, if you want to control caching behavior, you should be using an NSURLRequest. NSURLRequest allows you to customize the caching behavior of the request by setting the cachePolicy of the request. In your case, you want to use NSURLRequestReloadIgnoringLocalCacheData. An example of doing this synchronously using NSURLConnection would be:
NSString *result = nil;
NSData *data = nil;
NSURLResponse *response = nil;
NSURLError *error = nil;
NSURLRequest *request = [[NSURLRequest alloc] initWithURL:URL cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:20L];
data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
if (response != nil && [data length] > 0){
result = [NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
}
Note that this is a very naive implementation that does not check the HTTP status code returned, the mime-type of the response, or perform any error handling. It is also not a recommended practice to load network resources synchronously or to do so from the main thread. A better implementation would use sendAsynchronousRequest:completion: or NSURLSession.
However, it does demonstrate at a high level what you would need to do to answer your question: The NSURLRequest specifies that this request should never use the local cache, and the returned data is used to create an instance of NSString.
Simple cache-buster dummy parameter with random value added to URL should work.
And as #Josh-Caswell said, use NSURLRequest. Although in case of proxy servers, just using NSURLRequest may not help and you will still need cache-buster.
My app makes a lot of URL requests (calling a web service) in a loop for an extended period of time. When I watch the app in the Allocations tool, I see memory consumption going up continuously during the run of that loop. For testing purposes, I've reduced the loop to the following, which exhibits the same behavior:
NSURL *myUrl = [[NSURL alloc] initWithString:#"http://my.server.com/webservice"];
NSURLRequest request = [[NSURLRequest alloc] initWithURL:myUrl cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:30];
while (1)
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSURLResponse *response = nil;
NSError *error = nil;
NSData *rawResponse = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
[[NSURLCache sharedURLCache] removeAllCachedResponses];
[pool release];
}
When I first discovered the problem, I figured it was because my URL requests/responses were being cached. That's when I added the [[NSURLCache sharedURLCache] removeAllCachedResponses]. I was expecting that to clear out the cache after each call to the web service and free up any memory being used for URL caching purposes. No luck.
I must be doing something wrong, but I can't find it. Am I barking up the wrong tree in thinking it's the URL caching? What else could it be?
I'm currently doing this when populating core data from a JSON file:
NSString *urlString = [value objectForKey:#"url"];
NSURL *url = [NSURL URLWithString:urlString];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
NSURLResponse *response = nil;
NSError *error = nil;
NSData *dataResponse = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
[managedObject setValue:dataResponse forKey:#"image"];
Is there a better (asynchronous) way to do this with AFNetworking? What is the best method for this case? Does it have to be synchronous because we're dealing with CoreData?
UPDATE: Trying this now:
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[NSURLConnection sendAsynchronousRequest:request queue:queue completionHandler:^(NSURLResponse *response, NSData *data, NSError *error)
{
[managedObject setValue:data forKey:#"image"];
}];
For some reason when I access the managed object later, the image attribute is always null, even though *data above is not null in the completion handler. The image gets saved fine in the synchronous method. What am I missing?
NSURLConnection can deal with async too.
The method that you can use is (iOS >= 5) is
+ sendAsynchronousRequest:queue:completionHandler:
If you need to target iOS < 5 then use the delegate pattern for NSURLConnection. A good wrapper for this can be found in NSURLConnection and grand central dispatch.
About Core Data, I would say it depends. If data you need to store is cheap, do it in the main thread. On the contrary you have three different ways to do it:
(1) use new Core Data queue-based API (iOS >= 5)
(2) kick off a NSOperation within a NSOperationQueue and do the long work in background
(3) use GDC
Pay attention to Core Data constraints (threads constraints) when you deal with (2) or (3).
Hope that helps.
P.S. If you want to know something else let me know.
There's a sendAsynchronousRequest:queue:completionHandler: message of NSURLConnection.
Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
We don’t allow questions seeking recommendations for books, tools, software libraries, and more. You can edit the question so it can be answered with facts and citations.
Closed 4 months ago.
Improve this question
I would some suggestion about the choose of an iOS networking library for my app.
My needs are:
Send asynchronous request (GET and POST) and if the network is down display a UIAlertView to inform the user of the error.
Send simple synchronous request (GET) and if the network is down do the same of the above point.
Does anyone have some lib to suggest? (except ASIHTTPRequest that is no longer supported) Possibly, if this lib have some nice doc is better. I'm an iOS beginner.
Thanks for help in advance.
I've heard good things about RestKit https://github.com/RestKit/RestKit
There is a good list of alternatives at the bottom of this blog post. http://allseeing-i.com/[request_release];
I've only used ASIHTTPRequest before, and even though it's not longer being developed by Ben Copsey, it looks like he's still merging pull requests and stuff. It's in use by so many people, I wouldn't be surprised if the community picks it up. It will probably be safe for at least another version of iOS.
You don't really need a library for this.
To send a synchronous GET request:
//set up the GET URL and params
NSURL *getURL = [NSURL URLWithString:#"http://somesite.com/somepath?foo=bar"];
//create the request
NSURLRequest *request = [NSURLRequest requestWithURL:getURL];
//get the response
NSError *error = nil;
NSURLResponse *response = nil;
NSData *responseData = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
To send a synchronous POST request:
//set up the POST URL and params
NSURL *postURL = [NSURL URLWithString:#"http://somesite.com/somepath"];
NSString *postParams = #"foo=bar&hello=world";
//create the request - this bit is the same for every post
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:postURL];
[request setHTTPMethod:#"POST"];
[request addValue:#"application/x-www-form-urlencoded" forHTTPHeaderField:#"Content-Type"];
NSData *data = [postParams dataUsingEncoding:NSUTF8StringEncoding];
[self addValue:[NSString stringWithFormat:#"%i", [data length]] forHTTPHeaderField:#"Content-Length"];
[self setHTTPBody:data];
//get the response
NSError *error = nil;
NSURLResponse *response = nil;
NSData *responseData = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
In either case if responseData is nil or error is not nil, present an alert using the following:
[[[[UIAlertView alloc] initWithTitle:#"Error" message:[error localizedDescription] delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil] autorelease] show];
As stated in the question he is also looking for asynchronous requests.
I would suggest AFNetworking or GCNetworkKit (the last one is my own). Both libraries are very easy to use and yet powerful.
I don't think AFNetworking provides a synchronous network request though. Well at least mine doesn't. You shouldn't do that anyway.
Both libraries support error handling. There should be no problem on implementing an UIAlertView.
You can find them on GitHub. Just search for them. Hope this helps :)