So I am downloading some data from a server to my ios client. The data needs to be formatted so I use NSNotification to notify the app when the data has been downloaded completely and when thats done, I format the data and then display it on the screen.
All this is cool but because of the size of the data, the screen freezes.I thought I should use GCD to push the data downloading to another thread so the UI is still responsive. When i did that, I dont seem to be downloading any data.
I have a method getTops that uses NSURLConnection to download the data. Initially, in my viewDidLoad I called this method and it worked fine but then I used GCD like so
dispatch_queue_t getItemsQ = dispatch_queue_create("get Items", NULL);
dispatch_async(getItemsQ, ^{
[self getTops];
});
And it stopped working. I know it gets to the getTops because I can see the log in the console but it never reaches the -(void)connectionDidFinishLoading:(NSURLConnection *)connection
Here is the code i used:
-(void)getTops{
Keychain *keychain = [[Keychain alloc]init];
NSString *auth_token = [keychain getAuthToken];
NSLog(#"at getTops");
topimageArray = [[NSMutableArray alloc]init];
NSURLConnection *connection;
webData = [[NSMutableData alloc]init];
NSURL *url = [[NSURL alloc]initWithString:base];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc]init];
[request setURL: url];
[request setValue:auth_token forHTTPHeaderField:#"X-AUTH-TOKEN"];
connection = [NSURLConnection connectionWithRequest:request delegate:self];
// [connection start];
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
NSLog(#"at getTops conn start");
}
-(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response{
NSLog(#"recieved response");
[webData setLength:0];
}
-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data{
if(data) {
NSLog(#"Appending data");
[webData appendData:data];
}
}
-(void)connectionDidFinishLoading:(NSURLConnection *)connection{
NSArray *response= [NSJSONSerialization JSONObjectWithData:webData options:0 error:nil];
NSLog(#"Tops full response::: %#",response);
theArray = [[NSArray alloc]initWithArray:response];
NSLog(#"DONE");
//Notify that the data is ready to be formated
[[NSNotificationCenter defaultCenter]postNotificationName:#"getTops" object:nil];
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
}
-(void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error{
NSLog(#"error::::%#", error);
NSString *errormsg = error.localizedDescription;
UIAlertView *alertview = [[UIAlertView alloc]initWithTitle:#"Error" message:errormsg delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil, nil];
[alertview show];
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
}
I thought maybe I should remove the [UIApplication sharedApplication].networkActivityIndicatorVisible but that didnt help
Edit:: Added NSLogs to delegate methods.
The Log that I get is
at getTops conn start
And thats it.
The simplest way is to use sendAsynchronousRequest:queue:completionHandler:. No delegates are necessary, just put the code from connectionDidFinishLoading: in the completion block.
Loads the data for a URL request and executes a handler block on an operation queue when the request completes or fails.
[NSURLConnection sendAsynchronousRequest:request
queue:[NSOperationQueue mainQueue]
completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
< your code >
}];
Related
Suppose I call a webservice when the app is in foreground. Now if the user sends the app to background then how do I make sure that this webservice call keeps executing in the background.
This is the piece of code that I am using in my app.
Login* login = [[Login alloc]init];
[login initiateSignInProcess];
initiateSignInProcess has 4 web service calls. they are normal
functions. I am using AFNetworking.
If any of the services fail, I call it again with a delay in the failure block of afnetworking code like below:-
failure:^(AFHTTPRequestOperation *operation, NSError *error)
{
[self performSelector:#selector(getUserId) withObject:nil afterDelay:5];
}
Now I want to know that if the user sends the app to background, then how will the code execute? Will it call this function in bakcground till it succeeds?
Best to use Background Process for fetch. Here is great tutorial for solution [ http://code.tutsplus.com/tutorials/ios-7-sdk-working-with-background-fetch--mobile-20520
Not possible in iOS6.x or lesser unless your application is has specific requirement to run in background like locations, Voip, music etc...
However this is possible with iOS7, please consider having a look at this
http://redth.codes/ios7-recipe-background-fetching/
**For(large FIle Downloade use Asynchronous Method)**
NSURL *myUrl = [NSURL URLWithString:#"Enter URL HERE"];
NSURLRequest *myRequest = [NSURLRequest requestWithURL:myUrl cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:60];
NSMutableData *myData = [[NSMutableData alloc] initWithLength:0];
NSURLConnection *myConnection = [[NSURLConnection alloc] initWithRequest:myRequest delegate:self startImmediately:YES];
**For(Small FIle Downloade use Synchronous Method)**
NSURL *myUrl = [NSURL URLWithString:#"Enter URl HERE"];
NSData *myData = [NSData dataWithContentsOfURL:myUrl];
UIImage *img = [UIImage imageWithData:myData];
add NSURLConnection Delegate in .h File
- (void) connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
[myData setLength:0];
}
- (void) connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
[myData appendData:data];
}
- (void) connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
[connection release];
}
- (void) connectionDidFinishLoading:(NSURLConnection *)connection {
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
[connection release];
//download finished - data is available in myData.
}
This is depends on OS scheduling whether it allows continue to run the services in background or kill it.
Best to use Background Fetch. Here is nice tutorial http://code.tutsplus.com/tutorials/ios-7-sdk-working-with-background-fetch--mobile-20520
Hope this solve your issue.
I have to do SSL pinning so need to verify server side SSL certificate.
SO I have to use NSURL delegates. I have a helper class in which I have created method which returns me login response:
- (NSData *)sendSynchronousRequest:(NSString *)strNewLoginRequest
returningResponse:(NSURLResponse **)response
error:(NSError **)error {
NSMutableURLRequest *finalRequest = nil;
NSURL *url= [NSURL URLWithString:const_url];
finalRequest = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:30.0];
NSData *requestData = [NSData dataWithBytes:[strLoginRequest UTF8String] length:[strLoginRequest length]];
self.connection = [[NSURLConnection alloc] initWithRequest:finalRequest delegate:self startImmediately:NO];
NSRunLoop *currentRunLoop = [NSRunLoop currentRunLoop];
[self.connection unscheduleFromRunLoop:currentRunLoop forMode:NSDefaultRunLoopMode];
[self.connection scheduleInRunLoop:currentRunLoop forMode:#"connectionRunLoopMode"];
[self.connection start];
while ([currentRunLoop runMode:#"connectionRunLoopMode" beforeDate:[NSDate distantFuture]]);
return self.mutableResponse;
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
self.response = response;
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
self.mutableResponse = [[NSMutableData alloc]init];
[self.mutableResponse appendData:data];
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
dispatch_async(dispatch_get_main_queue(), ^{
if (loadingView)
{
[loadingView removeView];
}
UIAlertView *alert = [[UIAlertView alloc]initWithTitle:#"Failure" message:#"Network Failure" delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil, nil];
[alert show];
});
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
if (loadingView)
{
[loadingView removeView];
}
self.resultString = [[NSString alloc] initWithData:self.mutableResponse encoding:NSASCIIStringEncoding];
}
and I am calling this method from another class called ViewController with code
-(void)doLogin
{
self.service = [[SyncCommunicationService alloc]init];
NSData *data = [self.service sendSynchronousRequest:strNewLoginRequest
returningResponse:&response
error:nil];
}
I have tried calling this method in background and on main thread but still delegate methods are not getting called, I have tried many other answers from same website but still couldn't able to solve this issue so please can anybody have a clue what am I doing wrong.
I'm wondering why would anyone use asynchronous request for performing task synchronously? Not to mention this strange way to wait with while statement instead of dispatch_semaphore or something similar.
However, why You even bother with delegate? Just use class method sendSynchronousRequest:returningResponse:error:. I think, it would suffice in your case
I am using the Yelp Search API to basically just get a list of businesses for a search query.
It is pretty much a NSURLConnection is OAuth, but here is the code to initialize the request:
NSURL *URL = [NSURL URLWithString:appDelegate.yelpAdvancedURLString];
OAConsumer *consumer = [[OAConsumer alloc] initWithKey:#"this-is-my-key" secret:#"this-is-my-secret"];
OAToken *token = [[OAToken alloc] initWithKey:#"this-is-my-key" secret:#"this-is-my-secret"];
id<OASignatureProviding, NSObject> provider = [[OAHMAC_SHA1SignatureProvider alloc] init];
NSString *realm = nil;
OAMutableURLRequest *request = [[OAMutableURLRequest alloc] initWithURL:URL
consumer:consumer
token:token
realm:realm
signatureProvider:provider];
[request prepare];
responseData = [[NSMutableData alloc] init];
yelpConnection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
Then here:
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
[responseData setLength:0];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
[responseData appendData:data];
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
NSLog(#"Error: %#, %#", [error localizedDescription], [error localizedFailureReason]);
UIAlertView *alert = [[UIAlertView alloc] initWithTitle: #"Oops." message: #"Something screwed up. Please search again." delegate: nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alert show];
[MBProgressHUD hideHUDForView:self.view animated:YES];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
if (connection == self.yelpConnection) {
[self setYelpString];
}
}
When I run this on iPhone, everything is working fine. However, when I run on iPad, the connection gets timed out. The following is from this line
NSLog(#"Error: %#, %#", [error localizedDescription], [error localizedFailureReason]);
Error: The request timed out., (null)
Also if I use a synchronous request, it seems to work using this:
NSData* result = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
NSDictionary* JSON = [NSJSONSerialization
JSONObjectWithData:result
options:kNilOptions
error:&error];
However, I want to avoid using synchronous as it freezes the app.
Is this Yelp API specific? Or am I just doing something wrong? Thanks in advance, I would appreciate any help.
If it helps, it times out approximately 10 seconds after sending the request.
create this type of NSMutableURLRequest :
NSMutableURLRequest* request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:url] cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:240.0];
I think the best approach is to change the init method in http://oauth.googlecode.com from
- (id)initWithURL:(NSURL *)aUrl
consumer:(OAConsumer *)aConsumer
token:(OAToken *)aToken
realm:(NSString *)aRealm
signatureProvider:(id<OASignatureProviding, NSObject>)aProvider
{
if (self = [super initWithURL:aUrl
cachePolicy:NSURLRequestReloadIgnoringCacheData
timeoutInterval:10.0])
{
...
}
}
to
- (id)initWithURL:(NSURL *)aUrl
cachePolicy:(NSURLRequestCachePolicy)cachePolicy
timeoutInterval:(NSTimeInterval)timeoutInterval
consumer:(OAConsumer *)aConsumer
token:(OAToken *)aToken
realm:(NSString *)aRealm
signatureProvider:(id<OASignatureProviding, NSObject>)aProvider
{
if (self = [super initWithURL:aUrl
cachePolicy:cachePolicy
timeoutInterval:timeoutInterval])
{
...
}
and then check again, whether the timeout value which you specify will be honored by the connection.
Our requirements include checking Internet access to a specific file on the web-server. This file is checked every n minutes. NSURLRequests never calls connection:didFailWithError whether or not there is an internet connection. And the HTTP status is always 200. Apple's reachibility only works for domains, not files- so it doesn't meet the requirements. How can I reliably discover if I can reach this file every n minutes? Why isn't the http status code really the http status code?
Other stackoverflow questions that would seem to answer this question do not work:
1. How could connectionDidFinishLoading: run if no file is found on server?
2. Testing use of NSURLConnection with HTTP response error statuses
I tried using another queue with a completion block, but that also didn't work.
-(void) updateConnectionStatus
{
NSURL *url = [NSURL URLWithString:(NSString*)[appValues getValueForSettingsKey:#"company.project.test.pingURL"]];
NSURLRequest *urlRequest = [NSURLRequest requestWithURL:url];
//NSOperationQueue *queue = [[NSOperationQueue alloc] init];
//__block __typeof__(self) _self = self;
connection = [[NSURLConnection alloc] initWithRequest:urlRequest delegate:self];
/*
[NSURLConnection
sendAsynchronousRequest:urlRequest queue:queue
completionHandler:^(NSURLResponse *response,
NSData *data,
NSError *error) {
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse*)response;
int code = [httpResponse statusCode]; // ALWAYS 200 no matter what
NSString *pingFile = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(#"%#",error); // NEVER has an error
//This doesn't even work because it remembers FOREVER the value once it gets it.
if ([#"Ping!" isEqualToString:pingFile])
{
dispatch_async(dispatch_get_main_queue(), ^{
[_self companyConnection:YES];
});
} else {
dispatch_async(dispatch_get_main_queue(), ^{
[_self companyConnection:NO];
});
}
}];
*/
}
-(void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
NSLog(#"ERROR: %#", error); // Never get here
}
-(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
NSHTTPURLResponse *aResponse = (NSHTTPURLResponse*)response;
NSLog(#"received a response: %ld",(long)[aResponse statusCode] );
if ([response respondsToSelector:#selector(statusCode)])
{
int statusCode = [((NSHTTPURLResponse *)response) statusCode];
// statusCode is always 200
if (statusCode >= 400)
{
[companyConnection cancel]; // stop connecting; no more delegate messages
NSDictionary *errorInfo
= [NSDictionary dictionaryWithObject:[NSString stringWithFormat:
NSLocalizedString(#"Server returned status code %d",#""),
statusCode]
forKey:NSLocalizedDescriptionKey];
}
}
}
-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
NSLog(#"received data");
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
NSLog(#"Finished");
}
Try with setting cachePolicy as NSURLRequestReloadIgnoringCacheData while constructing the NSURLRequest object
Thanks to Wain and Rob for putting me onto the right path. One way to keep the cache clear is adding this method to your NSURLConnectionDelegate:
- (NSCachedURLResponse *)connection:(NSURLConnection *)connection willCacheResponse:(NSCachedURLResponse *)cachedResponse
{
return nil;
}
How can I upload/download data from a server in Cocoa Touch. Here's what I have so far...
-(void)uploadSchedule:(id)sender
{
NSData *content = [NSData dataWithContentsOfFile:self.dataFilePath];
NSString *stuff = [[NSString alloc] initWithData:content encoding:NSASCIIStringEncoding];
NSURL *url = [NSURL URLWithString:#"http://thetis.lunarmania.com"];
NSMutableURLRequest* urlRequest = [[NSMutableURLRequest alloc]initWithURL:url];
[urlRequest setHTTPMethod:#"POST"];
[urlRequest setHTTPBody:[stuff dataUsingEncoding:NSASCIIStringEncoding]];
NSLog(#"great success!");
}
-(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
// this method is called when the server has determined that it
// has enough information to create the NSURLResponse
// it can be called multiple times, for example in the case of a
// redirect, so each time we reset the data.
// receivedData is declared as a method instance elsewhere
[receivedData setLength:0];
}
-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
// append the new data to the receivedData
// receivedData is declared as a method instance elsewhere
[receivedData appendData:data];
}
-(void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
// release the connection, and the data object
[connection release];
// receivedData is declared as a method instance elsewhere
[receivedData release];
// inform the user
NSLog(#"Connection failed! Error - %# %#",
[error localizedDescription],
[[error userInfo] objectForKey:NSErrorFailingURLStringKey]);
}
-(void)connectionDidFinishLoading:(NSURLConnection *)connection
{
// do something with the data
UIImage *image = [[UIImage alloc] initWithData:receivedData];
[cat setImage:image];
[image release];
// receivedData is declared as a method instance elsewhere
NSLog(#"Succeeded! Received %d bytes of data",[receivedData length]);
// release the connection, and the data object
[connection release];
[receivedData release];
}
-(void)connection:(NSURLConnection *)connection
didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
{
if ([challenge previousFailureCount] == 0) {
NSURLCredential *newCredential;
newCredential=[NSURLCredential credentialWithUser:#"ican#moeyo.org"
password:#"icanican"
persistence:NSURLCredentialPersistenceNone];
[[challenge sender] useCredential:newCredential
forAuthenticationChallenge:challenge];
} else {
[[challenge sender] cancelAuthenticationChallenge:challenge];
// inform the user that the user name and password
// in the preferences are incorrect
//[self showPreferencesCredentialsAreIncorrectPanel:self];
}
}
I'm so lost...
The code crashes because you over-release connection. Review the Cocoa memory management rules.
Aside from that, you'll have to be more specific about what problem you're having with it.
BTW, the term is “instance variable”, not “method instance”. An instance variable is a variable inside of an instance, and has nothing to do with methods.
This has been covered here:
NSURLRequest - encode url for NSURLRequest POST Body (iPhone objective-C)
The accepted answer uses ASIHTTPRequest which is similar to one I've used, and makes it really easy to post/get from an HTML form. Here's an example (from past stackoverflow)
ASIFormDataRequest *request = [[[ASIFormDataRequest alloc] initWithURL:#"http://someSite.com"] autorelease];
[request setPostValue:#"myValue1" forKey:#"myFormField1"];
[request setPostValue:#"myValue2" forKey:#"myFormField2"];
// etc.
[request start];
NSError *error = [request error];
if (!error)
NSString *response = [request responseString];
And if your file is big, you should better use NSFilehandle, to write data inside didReceiveData, instead of appending.