I try to retrive data from certain url with command:
-(NSMutableData *) callUrl: (NSString *)url withData:(NSMutableDictionary *)data delegate:(id) delegate {
NSURL *executeUrl = [NSURL URLWithString:<string>];
NSURLRequest *request = [NSURLRequest requestWithURL: executeUrl
cachePolicy:NSURLRequestReloadIgnoringLocalCacheData
timeoutInterval:60];
NSMutableData *receivedData = nil;
NSURLConnection *theConnection=[[NSURLConnection alloc] initWithRequest:request delegate:delegate];
if (theConnection) {
receivedData = [[NSMutableData data] retain];
} else {
#throw #"Connection error";
}
return receivedData;
}
In delegate (both after connectionDidFinish and connectionDidFailWithError) I do:
//some uninvasive alerts
// release the connection, and the data object
[connection release];
[receivedData release];
Problem is when I provide bad url I got proper error - it's good part - but then I want to execute second url - good for sure, I've got 1003 error - NSURLErrorCannotFindHost.
After around 1-2 min I'm succesfully call url and get data.
I suspect some timeouts and ports business, but changing timeout in NSURLRequest doesn't change a thing.
UPDATE
As it turned out - Administrators had some issues with DNS server reached through WiFi network. Code is fine. Thanks for response.
If some has similiar problems: try ip address instead of hostname.
From Apple iOS developer documentation, 1003 error refers to when the host name for a URL cannot be resolved. To avoid DNS failures in wifi, overloaded DNS scenarios, it is preferable to resolve ip from hostname for subsequent use or to hardcode the ip address directly, if you do not intend to shift the hosting later on.
Apple documentation:
URL Loading System Error Codes
These values are returned as the error code property of an NSError object with the domain “NSURLErrorDomain”.
enum
{
NSURLErrorBadURL = -1000,
NSURLErrorTimedOut = -1001,
NSURLErrorUnsupportedURL = -1002,
NSURLErrorCannotFindHost = -1003,//****
NSURLErrorCannotConnectToHost = -1004,
NSURLErrorDataLengthExceedsMaximum = -1103,
NSURLErrorNetworkConnectionLost = -1005,
NSURLErrorDNSLookupFailed = -1006,
...
}
1003 NSURLErrorCannotFindHost
Returned when the host name for a URL cannot be resolved.
Available in iOS 2.0 and later.
Declared in NSURLError.h.
I did 2 things to fix this issue :
I used this before initiating my NSUrlConnection
[NSURLConnection cancelPreviousPerformRequestsWithTarget:self];
I changed my DNS to 8.8.8.8 in wifi settings which is google's public DNS server.
Don't know which one fixed it but the issue was resolved.
before making any new connection call cancel previous connection.
using
[self.connection cancel];
Related
I currently am using AFNetworking to determine if my application has network reachability.
NSNumber *s = notification.userInfo[AFNetworkingReachabilityNotificationStatusItem];
AFNetworkReachabilityStatus status = [s integerValue];
if (status == AFNetworkReachabilityStatusReachableViaWWAN || status == AFNetworkReachabilityStatusReachableViaWiFi) {
But, now I also need to know if my application can reach a specific server. More specifically, the server I am connecting to may be down and I need a way to determine if this is the case, from the client side, so I can notify my users appropriately.
It's a very tough google because all searches I do just point me to "How to determine network reachability". Has anybody dealt with this before, and have a solution in mind?
EDIT: #AvT recommended a promising looking solution, so I tried it like this:
self.testTSCReachabilityManager = [AFNetworkReachabilityManager managerForDomain:#"www.asdasfjsldfkjslefjslkjslfs.com"];
__weak MyObject *weakSelf = self;
[self.testReachabilityManager setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) {
if (weakSelf.testReachabilityManager.reachable) {
NSLog(#"YES");
}
else
{
NSLog(#"NO");
}
}];
But unfortunately, it is logging out "YES" for me, even after I have confirmed it is most definitely not reachable.
Instantiate AFNetworkReachabilityManager with class method
+ (instancetype)managerForDomain:(NSString *)domain;
and pass string with the required domain. AFNetworkReachabilityManager will check reachability of this domain.
If serverURL is an url of your server you should use it the following way:
[AFNetworkReachabilityManager managerForDomain:serverURL.host]
Update
Following code works as expected:
static AFNetworkReachabilityManager *testTSCReachabilityManager;
testTSCReachabilityManager = [AFNetworkReachabilityManager managerForDomain:#"www.asdasfjsldfkjslefjslkjslfs.com"];
[testTSCReachabilityManager setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) {
if (testTSCReachabilityManager.reachable) {
NSLog(#"YES");
}
else
{
NSLog(#"NO");
}
}];
[testTSCReachabilityManager startMonitoring];
Update: I actually ended up going w/ a different implementation than what Avt recommended, and did what matt recommended in the comments instead
I created an NSURLRequest and make a request to my server, then used the delegate callbacks to determine if the server was reachable. Works like a charm
-(void)checkConnectionToServers
{
NSMutableURLRequest* request = [NSMutableURLRequest new];
request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:#"www.myserver.com"] cachePolicy:0 timeoutInterval:(NSTimeInterval)5.0];
[request setHTTPMethod:#"GET"];
[NSURLConnection connectionWithRequest:request delegate:self];
}
-(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
NSLog(#"SUCCESS");
}
-(void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
NSLog(#"FAIL");
}
I'm using this code:
NSString *recievedData;
NSURLRequest *theRequest=[NSURLRequest requestWithURL:[NSURL URLWithString:#"http://www.site.com/"]
cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:60.0];
// create the connection with the request
// and start loading the data
NSURLConnection *theConnection=[[NSURLConnection alloc] initWithRequest:theRequest delegate:self];
if (theConnection) {
// Create the NSMutableData to hold the received data.
// receivedData is an instance variable declared elsewhere.
recievedData = [NSMutableData data];
NSLog(#"%#", recievedData);
} else {
// Inform the user that the connection failed.
NSLog(#"Unsuccessful.");
}
It's a modified version of this.
My problem is that it always returns
<>
Whether I'm connected to the internet or not, it always goes to successful.
You haven't received any data, you have just instantiated the object that will hold the received data. You need to implement the delegate methods for handling responses and failures and it is usually best to use NSURLConnection asynchronously.
There is some example code Using NSURLConnection
NSURLConnection doesn't work that way. You start a connection and then receive callbacks as data is received.
If you want a simple call to retrieve remote data, use NSData's dataWithContentsOfURL method. However, you should only use that on secondary threads because otherwise it will lock up your user interface for the duration of the call and the system may terminate your app if it takes too long.
See the full code at NSURLConnection example.
How to access the (POST)data sent with the request from the requestFailed/requestFinished function.
- (void) abc {
NSString *postString = #"john";
NSURL *url = [NSURL URLWithString:#"http://abc.com"];
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
[request setPostValue:postString forKey:#"name"];
[request setDelegate:self];
[request startAsynchronus];
}
- (void) requestFinished:(ASIHTTPRequest *)request
{
// Question is whether the request holds the sent post values.
// If it holds. how can we access them.
// i tried using [request valueForKey:#"name"];
// but it won't work.
}
Handling success and failure for multiple requests in delegate methods
If you need to handle success and failure on many different types of
request, you have several options:
If your requests are all of the same broad type, but you want to
distinguish between them, you can set the userInfo NSDictionary
property of each request with your own custom data that you can read
in your finished / failed delegate methods. For simpler cases, you can
set the request’s tag property instead. Both of these properties are
for your own use, and are not sent to the server.
If you need to handle success and failure in a completely different way for each
request, set a different setDidFinishSelector / setDidFailSelector for
each request For more complex situations, or where you want to parse
the response in the background, create a minimal subclass of
ASIHTTPRequest for each type of request, and override requestFinished:
and failWithError:.
That provided me a good solution to handle different requests.
You could try this -
- (void)requestFinished:(ASIHTTPRequest *)request {
NSLog(#"Response %d ==> %#", request.responseStatusCode, [request responseString]);
}
You can also handle other methods if you choose, such as:
- (void)requestStarted:(ASIHTTPRequest *)request;
- (void)requestFailed:(ASIHTTPRequest *)request;
The docs are located at http://allseeing-i.com/ASIHTTPRequest/ and are fantastic.
You can cast your request into a ASIFormDataRequest:
if ([request isKindOfClass:[ASIFormDataRequest class]]) {
ASIFormDataRequest *requestWithPostDatas = (ASIFormDataRequest *)request;
NSArray *myPostData = [requestWithPostDatas getPostData];
}
You will also have to make "postData" accessible with a "getPostData" public function in ASIFormDataRequest.
Whenever I do a curl call using the below code:
NSURL *url = [NSURL URLWithString:requestURL];
NSURLRequest *request = [NSURLRequest requestWithURL:url
cachePolicy:NSURLRequestReloadIgnoringLocalCacheData
timeoutInterval:30];
if (connectionInProgress) {
[connectionInProgress cancel];
}
connectionInProgress = [[NSURLConnection alloc]initWithRequest:request delegate:self startImmediately:YES];
connectionDidFinishLoading is my final destination where I can manipulate the response data and call my next methods to continue with the app . If I hard-code some specific tasks like
-(void)connectionDidFinishLoading:(NSURLConnection *)connection
{
NSXMLParser *parser = [[NSXMLParser alloc] initWithData:xmlData];
[parser setDelegate:self];
[parser parse];
[someLabel setText:parsedTextFromXMLData];
}
If I need to do another curl call to a different address, wouldn't someLabel setText always get re-set again? Is there a way to make this delegate function behave differently on each curl call? (btw, is connectionDidFinishLoading usually the right place to put the next step of codes?) If so then wouldn't it always get called again by the next curl call?
Have a look at this S.O. post for a recipe concerning NSURLConnection and multiple requests.The suggestion is doing something like this:
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
if (connection == firstConnection) {
// do something
}
else if (connection == secondConnection) {
// do something else
}
}
EDIT: the idea here is that connectionDidFinishLoading is a method of your own delegate (so you write it). In the delegate, you store the address of each connection you create; then, when the connection comes back with the data, you tell which connection it is by comparing its address to the one you stored in the delegate. -END EDIT
Another option you have is using the ASIHTTPRequest framework, which offers a request-based (as opposed to connection-based) delegation mechanism, so each request has got a delegate object to handle the result; or, in other words, the delegate receives a reference to the request, so you can easily tell which request result you are handling.
ASIHTTPRequest offers a bunch of advantages over NSURLConnection. You can read about them in this S.O. post.
There're 2 options to do this:
you can implement a separate class, that will be responsible for handling NSURLConnection delegate stuff and create a separate instance for each request
you can use NSObject key-value methods on NSURLConnection instance for setting up some tag, that will be checked in connectionDidFinishLoading: method
For me, option 1 will be a better approach
On iPhone, I perform a HTTP request using NSURLRequest for a chunk of data. Object allocation spikes and I assign the data accordingly. When I finish with the data, I free it up accordingly - however instruments doesn't show any data to have been freed!
My theory is that by default HTTP requests are cached, however - I don't want my iPhone app to cache this data.
Is there a way to clear this cache after a request or prevent any data from being cached in the first place?
I've tried using all the cache policies documented a little like below:
NSMutableURLRequest *theRequest = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:url]];
theRequest.cachePolicy = NSURLRequestReloadIgnoringLocalCacheData;
but nothing seems to free up the memory!
Usually it's easier to create the request like this
NSURLRequest *request = [NSURLRequest requestWithURL:url
cachePolicy:NSURLRequestReloadIgnoringCacheData
timeoutInterval:60.0];
Then create the connection
NSURLConnection *conn = [NSURLConnection connectionWithRequest:request
delegate:self];
and implement the connection:willCacheResponse: method on the delegate. Just returning nil should do it.
- (NSCachedURLResponse *)connection:(NSURLConnection *)connection willCacheResponse:(NSCachedURLResponse *)cachedResponse {
return nil;
}
I have the same problem in my app when I requested info from twitter. In my case I didn't need to preserve those credentials, so I simple erase them using the next code:
- (void) eraseCredentials{
NSURLCredentialStorage *credentialsStorage = [NSURLCredentialStorage sharedCredentialStorage];
NSDictionary *allCredentials = [credentialsStorage allCredentials];
//iterate through all credentials to find the twitter host
for (NSURLProtectionSpace *protectionSpace in allCredentials)
if ([[protectionSpace host] isEqualToString:#"twitter.com"]){
//to get the twitter's credentials
NSDictionary *credentials = [credentialsStorage credentialsForProtectionSpace:protectionSpace];
//iterate through twitter's credentials, and erase them all
for (NSString *credentialKey in credentials)
[credentialsStorage removeCredential:[credentials objectForKey:credentialKey] forProtectionSpace:protectionSpace];
}
}
I hope it works for somebody :)
If you use NSURLConnection take a look at the delegate:
- (NSCachedURLResponse *)connection:(NSURLConnection *)connection willCacheResponse:(NSCachedURLResponse *)cachedResponse
Return Value
The actual cached response to store in the cache. The delegate may return cachedResponse unmodified, return a modified cached response, or return nil if no cached response should be stored for the connection.
If you're using NSURLSession, another solution to prevent request and parameters being written to the Cache.db iOS creates within the app's Caches directory, is to set the NSURLCache for the session's configuration to a 0 size memory and 0 size disk cache e.g.
let configuration = URLSessionConfiguration.default
configuration.urlCache = URLCache(memoryCapacity: 0, diskCapacity: 0, diskPath: nil)
let session = URLSession(configuration: configuration)
or as mentioned above set at a global cache level
URLCache.shared = URLCache(memoryCapacity: 0, diskCapacity: 0, diskPath: nil)
Presumably it's the 0 for disk size that stops iOS writing to disk but if you have a policy to reloadIgnoringLocalCacheData then you probably aren't interested in memory caching either.
Note This will prevent any Caches/Cache.db (requests & responses) or Caches/fsCachedData/ folder (response data) being created at all. We've decided to take this approach in an app for security purposes as we don't want our requests to be stored on disk cache ever.
If anyone knows is there's a way to stop only request caching but keep response data caching from the iOS URL Loading mechanism, I'd be interested to know. (there's no API or official documentation about this from what I can tell)
If not specific to a single request(U want disable cache for whole app) below one is the best option.Add this code in app delegate or based on ur need any where
int cacheSizeMemory = 0; // 0MB
int cacheSizeDisk = 0; // 0MB
NSURLCache *sharedCache = [[NSURLCache alloc] initWithMemoryCapacity:cacheSizeMemory diskCapacity:cacheSizeDisk diskPath:#"nsurlcache"];
[NSURLCache setSharedURLCache:sharedCache];
NSMutableURLRequest* request = [[NSMutableURLRequest alloc] url];
[request setValue:#"no-store" forHTTPHeaderField:#"Cache-Control"];
[request setCachePolicy:NSURLRequestReloadIgnoringLocalCacheData];
Assuming the server is correctly implemented, putting the Cache-Control:no-store header in the request will generate a server response with the same header, thus causing NSURLCache to not store the response data on disk.
Therefore, no need for the shotgun approach of disabling NSURLCache disk caching.
PS: Adding the header should work for all HTTP frameworks, like AFNetworking