Synchronous downloading with NSURLConnection and progress callback? - ios

I'm trying to implement synchronous downloading with progress callback with NSURLConnection.
When [connection start] is invoked, nothing happens - delegate callback methods are not just invoked (i'm testing on OSX in XCTestCase). What's wrong?
// header
#interface ASDownloadHelper : NSObject <NSURLConnectionDelegate, NSURLConnectionDataDelegate>
{
NSMutableData *_receivedData;
NSUInteger _expectedBytes;
id<ASDownloadHelperListener> _listener;
NSError *_error;
BOOL _finished;
id _finishedSyncObject;
}
- (void) download: (NSString*)url file:(NSString*)file listener:(id<ASDownloadHelperListener>)listener;
#end
// impl
#implementation ASDownloadHelper
// delegate
- (void) connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
[_receivedData setLength:0];
_expectedBytes = [response expectedContentLength];
}
- (void) connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
[_receivedData appendData:data];
int percent = round( _receivedData.length * 100.0 / _expectedBytes );
[_listener onDownloadProgress:_receivedData.length total:_expectedBytes percent:percent];
}
- (void) connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
_error = error;
[self setFinished:YES];
}
- (NSCachedURLResponse *) connection:(NSURLConnection*)connection
willCacheResponse:(NSCachedURLResponse*)cachedResponse {
return nil;
}
- (void) connectionDidFinishLoading:(NSURLConnection *)connection {
[self setFinished: YES];
}
- (BOOL) isFinished {
#synchronized(_finishedSyncObject) {
return _finished;
}
}
- (void) setFinished: (BOOL)finished {
#synchronized(_finishedSyncObject) {
_finished = finished;
}
}
// ---
- (void) download: (NSString*)downloadUrl file:(NSString*)file listener:(id<ASDownloadHelperListener>)listener {
_listener = listener;
_finished = NO;
_finishedSyncObject = [[NSObject alloc] init];
_error = nil;
NSURL *url = [NSURL URLWithString:downloadUrl];
NSURLRequest *request = [NSURLRequest requestWithURL:url
cachePolicy:NSURLRequestReloadIgnoringLocalCacheData
timeoutInterval:30];
_receivedData = [[NSMutableData alloc] initWithLength:0];
NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request
delegate:self];
[connection start];
// block the thread until downloading finished
while (![self isFinished]) { };
// error?
if (_error != nil) {
#throw _error;
return;
}
// success
[_receivedData writeToFile:file atomically:YES];
_receivedData = nil;
}
#end

Your wait loop lock the CPU main thread to 100%, patch your wait loop with :
...
// block the thread until downloading finished
while (![self isFinished])
{
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
};
...

Thanks to quellish i've found out that invocation queue should not be blocked as callback invocations (delegate methods) are done in invoker thread context. In my case i was running it in main test thread so i had to do workaround (and sleep in main thread for few seconds to let downloading finish):
- (void)testDownload
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void) {
// ...
[_downloadHelper download:repositoryUrl file:downloadedFile listener:downloadlistener];
// progress callbacks are invoked in this thread context, so it can't be blocked
// ...
XCTAssertNotNil( ... );
});
// block main test queue until downloading is finished
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:10]];
}

Related

NSURLConnection and NSURLConnectionDataDelegate: connection callbacks are not fired

I want to implement file downloading with progress from my server.
I my code I'm using a custom class which is delegated by
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:#"http://example.com"]];
if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(#"6.0")) {
DownloadCallback *dc = [[DownloadCallback alloc] initWithCallbackProgress:^(long long res){
NSLog(#"%lld", res);
} withCallbackReady:^(long long res){
NSLog(#"READY %lld", res);
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
}];
} withCallbackError:^(NSError * error) {
NSLog(#"READY %#", error.domain);
}];
NSURLConnection *connection = [NSURLConnection connectionWithRequest:request delegate:dc];
// [connection setDelegateQueue:[[NSOperationQueue alloc] init]];
[connection start];
header:
#interface DownloadCallback: NSObject<NSURLConnectionDataDelegate>{
#private void (^_progressHandler)(long long someParameter);
#private void (^_readyHandler)(long long someParameter);
#private void (^_errorHandler)(NSError *someParameter);
}
-(id) initWithCallbackProgress:(void(^)(long long))handler withCallbackReady:(void(^)(long long))handlerReady withCallbackError:(void(^)(NSError*))handlerError;
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response;
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data;
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error;
#end
body:
#implementation DownloadCallback
-(id) initWithCallbackProgress:(void(^)(long long))handler withCallbackReady:(void(^)(long long))handlerReady withCallbackError:(void(^)(NSError*))handlerError{
self = [super init];
if (self) {
_progressHandler = [handler copy];
_readyHandler = [handlerReady copy];
_errorHandler = [handlerError copy];
}
return self;
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
// self.expectedTotalSize = response.expectedContentLength;
// Call completion handler.
if (_readyHandler != nil)
_readyHandler(response.expectedContentLength);
// Clean up.
// [_completionHandler release];
_readyHandler = nil;
_progressHandler = nil;
_errorHandler = nil;
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
// self.recievedData += data.length;
if (_progressHandler != nil)
_progressHandler(data.length);
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
if (_errorHandler != nil)
_errorHandler(error);
}
#end
But the callback events are not fired! At all!
The simple synch code work prefectly:
// Send a synchronous request
NSURLRequest * urlRequest = [NSURLRequest requestWithURL:[NSURL URLWithString:#"http://example.com"]];
NSURLResponse * response = nil;
NSError * error = nil;
NSData * data = [NSURLConnection sendSynchronousRequest:urlRequest
returningResponse:&response
error:&error];
if (error == nil) {
// Parse data here
}
But I need a callback! How to resolve it? I've not found in stackoverflow a solution.
Futhermore, if I'm using a simple delegate to major class instead of DownloadCallback the same: the connection callbacks are not fired too.
Add the dealloc method to your callback class and out a breakpoint or log statement in it. See if it is deallocated before the callbacks are called.
If this is the case, your callback class instance is destroyed too soon. Make it a property of a class that will for sure live longer then the request.
Also, you should make sure that this code:
NSURLConnection *connection = [NSURLConnection connectionWithRequest:request delegate:dc];
[connection start];
is called on a thread outlives the connection and has a runloop. The easiest way to achieve this is to call that code on the main-queue. Your code-example does not show on which queue that is called. If it is not working I assume it is because your calling it on a background queue.
You can dispatch to a background queue from the delegate callbacks of you want/need to.
As a sidenote, if you are building something new, you should try and use NSURLSession instead of NSURLConnection. NSURLSession is more secure, easier to use and not deprecated. NSURLConnection is deprecated.

NSOperation fails to finish

I am trying to use an NSOperation started from another NSOperation
This is the inner most NSOperation:
#pragma mark - OVERRIDE
- (void)main
{
#autoreleasepool {
if (self.isCancelled) {
return;
}
NSURL *url = [NSURL URLWithString:self.urlStr];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
self.urlConnection = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:YES];
}
}
#pragma mark - NSURLConnectionDataDelegate
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
// NEVER CALLED
if (self.isCancelled) {
[connection cancel];
self.receivedData = nil;
return;
}
[self.receivedData appendData:data];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{// NEVER CALLED
if (self.isCancelled) {
self.receivedData = nil;
return;
}
// return data to the delegate
}
The problem is that although the main method gets called the didReceiveData neverGetsCalled.
I start this operation here:
- (void)main
{
#autoreleasepool {
if (self.isCancelled) {
return;
}
NSOperationGetJSONFromWeb *webGetOp = [[NSOperationGetJSONFromWeb alloc] initWithURLString:self.urlStr andDelegate:self];
[self.opQueue addOperation:webGetOp];
}
}
This is from another NSOperation obviously.
What is the problem here?
If I call this operation directly (not from another operation) everything works fine.
Use KVO flag like
#pragma mark - OVERRIDE
- (void)main
{
#autoreleasepool {
if (self.isCancelled) {
return;
}
NSURL *url = [NSURL URLWithString:self.urlStr];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
[self willChangeValueForKey:#"isExecuting"];
self.urlConnection = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:YES];
executing = YES;
[self didChangeValueForKey:#"isExecuting"];
}
}
#pragma mark - NSURLConnectionDataDelegate
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
// NEVER CALLED
if (self.isCancelled) {
[connection cancel];
self.receivedData = nil;
return;
}
[self.receivedData appendData:data];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{// NEVER CALLED
[self willChangeValueForKey:#"isFinished"];
[self willChangeValueForKey:#"isExecuting"];
executing = NO;
finished = YES;
[self didChangeValueForKey:#"isExecuting"];
[self didChangeValueForKey:#"isFinished"];
if (self.isCancelled) {
self.receivedData = nil;
return;
}
// return data to the delegate
}

IOS7 NSURLConnection nothing happens

I am quite a newbie to IOS programming and want to user's location to server. I followed iOS developer library and tutorials on the Internet but nothing happens when I run the code. I am using the iOS simulator and it gives me mock locations well and I get NSURLConnection instance. However, nothing happens on server-side.
#import "JLSViewController.h"
#import <CoreLocation/CoreLocation.h>
#import "JLSEventData.h"
#interface JLSViewController ()
#property (nonatomic, strong) CLLocationManager *locationManager;
#property (nonatomic, strong) NSMutableArray *locations;
#end
#implementation JLSViewController
NSMutableData *responseData = nil;
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.locations = [[NSMutableArray alloc] init];
self.locationManager = [[CLLocationManager alloc] init];
self.locationManager.desiredAccuracy = kCLLocationAccuracyBest;
self.locationManager.delegate = self;
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (IBAction)enabledStateChanged:(id)sender
{
if (self.switchEnabled.on)
{
[self.locationManager startUpdatingLocation];
}
else
{
[self.locationManager stopUpdatingLocation];
}
}
- (void)beginBackgroundUpdateTask: (CLLocation *)location {
}
- (void)endBackgroundUpdateTask {
}
#pragma mark - CLLocationManagerDelegate
- (void)locationManager:(CLLocationManager *)manager
didUpdateToLocation:(CLLocation *)newLocation
fromLocation:(CLLocation *)oldLocation {
if (newLocation == nil) {
return;
}
if (oldLocation != nil) {
if (newLocation.coordinate.latitude == oldLocation.coordinate.latitude &&
newLocation.coordinate.longitude == oldLocation.coordinate.longitude) {
return;
}
}
NSError *error = nil;
JLSEventData *eventData = [[JLSEventData alloc] initWithLocation: newLocation];
NSString *strData = [eventData toJSON:error];
if (error != nil){
return;
}
if (self.locations.count>5)
[self.locations removeObjectAtIndex:0];
[self.locations addObject:strData];
NSData *requestBodyData = [NSJSONSerialization dataWithJSONObject:self.locations options:NSJSONWritingPrettyPrinted error:&error];
if (error != nil){
return;
}
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
//[self beginBackgroundUpdateTask:newLocation];
// Send a synchronous request
NSMutableURLRequest * urlRequest = [NSMutableURLRequest requestWithURL:[NSURL URLWithString: "http://localhost:8080/mywebservice"]];
[urlRequest setValue:#"application/json; charset=utf-8" forHTTPHeaderField:#"Content-Type"];
[urlRequest setValue:#"application/json" forHTTPHeaderField:#"Accept"];
[urlRequest setHTTPMethod:#"POST"];
//[urlRequest setTimeoutInterval:20];
//NSJSONSerialization dataWithJSONObject
urlRequest.HTTPBody = requestBodyData;
// Create url connection and fire request
[urlRequest setHTTPBody:[NSData dataWithBytes:[strData UTF8String] length:strlen([strData UTF8String])]];
NSURLConnection *conn = [[NSURLConnection alloc] initWithRequest:urlRequest delegate:self];
if (conn!=nil){
//receivedData = nil;
}
});
}
#pragma mark - NSURLConnection Delegate
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
NSLog(#"==>didReceiveResponse");
// A response has been received, this is where we initialize the instance var you created
// so that we can append data to it in the didReceiveData method
// Furthermore, this method is called each time there is a redirect so reinitializing it
// also serves to clear it
responseData = [[NSMutableData alloc] init];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
NSLog(#"==>didReceiveData");
// Append the new data to the instance variable you declared
[responseData appendData:data];
}
- (NSCachedURLResponse *)connection:(NSURLConnection *)connection
willCacheResponse:(NSCachedURLResponse*)cachedResponse {
// Return nil to indicate not necessary to store a cached response for this connection
return nil;
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
// The request is complete and data has been received
// You can parse the stuff in your instance variable now
NSLog(#"Succeeded! Received %d bytes of data",[responseData length]);
responseData = nil;
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
// The request has failed for some reason!
// Check the error var
NSLog(#"Connection failed! Error - %# %#",
[error localizedDescription],
[[error userInfo] objectForKey:NSURLErrorFailingURLStringErrorKey]);
}
/*
*
*/
- (void)parseData {
//Do something
}
#end
Any help would be appreciated.
You are doing the NSURLConnection on a separate thread. And the thread is exiting before the response is received. You either need to do the NSURLConnection on the main thread or you need to keep the thread running until the response is received.

Issue in NSURL in Objective C

I am trying to execute a async http request. but the call back log is not working. please analyze the code and suggest me the cause of this issue. I have seen the class examples in many places. But here i am calling it from a main function.
#interface HTTP : NSObject
#property (nonatomic,retain) NSMutableData *receivedData;
- (void) get : (NSString *) urlString;
#end
#implementation HTTP
#synthesize receivedData;
- (void)get: (NSString *)urlString {
NSLog ( #"GET: %#", urlString );
self.receivedData = [[NSMutableData alloc] init];
NSURLRequest *request = [[NSURLRequest alloc]
initWithURL: [NSURL URLWithString:urlString]
cachePolicy: NSURLRequestReloadIgnoringLocalCacheData
timeoutInterval: 10
];
NSURLConnection *connection = [[NSURLConnection alloc]
initWithRequest:request
delegate:self
startImmediately:YES];
[connection start];
}
- (void)connection:(NSURLConnection*) connection didReceiveResponse:(NSURLResponse *)response
{
NSLog(#"Response recieved");
}
- (void)connection:(NSURLConnection*) connection didReceiveData:(NSData *)data
{
NSLog(#"Data recieved");
NSString* responseString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
[receivedData appendData:responseString];
}
#end
int main(const int c , char *arg[]){
HTTP *http = [[HTTP alloc] init];
[http get:#"http://www.apple.com"];
return 0;
}
Your program does not have a "run loop", therefore it terminates immediately after
[http get:#"http://www.apple.com"];
has returned, before any delegate functions are called. (Note that NSURLConnection works asynchronously.)
If this is for a stand-alone OS X application, you could to the following:
int main(const int c , char *arg[]){
HTTP *http = [[HTTP alloc] init];
[http get:#"http://www.apple.com"];
NSRunLoop *theRL = [NSRunLoop currentRunLoop];
while (shouldKeepRunning && [theRL runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]);
return 0;
}
where shouldKeepRunning is a (global) Boolean variable that is initially YES, and set to NO in
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
shouldKeepRunning = NO;
}
and also in connection:didFailWithError:. Or you add a Boolean property loading to your HTTP class.
If this is for an iOS application or a OS X Cocoa application, then you already have a run loop and don't have to add your own.
/*
Till the application finishes loading, the main thread is kept alive so that the delegate methods are called.
Hence the while loop below.
*/
while(!finished) {
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
}
Here is my working code.
#implementation HTTP
#synthesize receivedData,retStr,delegate;
- init {
if ((self = [super init])) {
receivedData = [[NSMutableData alloc] init];
}
return self;
}
- (void)get: (NSString *)urlString {
NSLog ( #"GET: %#", urlString );
self.receivedData = [[NSMutableData alloc] init];
NSURLRequest *request = [[NSURLRequest alloc]
initWithURL: [NSURL URLWithString:urlString]
cachePolicy: NSURLRequestReloadIgnoringLocalCacheData
timeoutInterval: 10
];
NSURLConnection *connection = [[NSURLConnection alloc]
initWithRequest:request
delegate:self
startImmediately:YES];
while(!finished) {
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
}
if(!connection) {
NSLog(#"connection failed :(");
} else {
NSLog(#"connection succeeded :)");
}
}
- (void)post:(NSString*)urlString: (NSString*)body: (NSObject*) sender {
// POST
NSMutableString* requestURL = [[NSMutableString alloc] init];
[requestURL appendString:urlString];
NSMutableString* requestBody = [[NSMutableString alloc] initWithString:body];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL: [NSURL URLWithString: [NSString stringWithString:requestURL]]];
NSString* requestBodyString = [NSString stringWithString:requestBody];
NSData *requestData = [NSData dataWithBytes: [requestBodyString UTF8String] length: [requestBodyString length]];
[request setHTTPMethod: #"POST"];
[request setValue:#"text/html; charset=utf-8" forHTTPHeaderField:#"Content-Type"];
[request setHTTPBody: requestData];
NSURLConnection *postConn= [[NSURLConnection alloc] initWithRequest:request delegate:sender];
/*
Till the application finishes loading, the main thread is kept alive so that the delegate methods are called.
Hence the while loop below.
*/
while(!finished) {
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
}
if(!postConn) {
NSLog(#"POST connection failed :(");
} else {
NSLog(#"POST connection succeeded :)");
}
}
// ====================
// Callbacks
// ====================
#pragma mark NSURLConnection delegate methods
- (NSURLRequest *)connection:(NSURLConnection *)connection
willSendRequest:(NSURLRequest *)request
redirectResponse:(NSURLResponse *)redirectResponse {
NSLog(#"Connection received data, retain count");
return request;
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
NSLog(#"Received response: %#", response);
[receivedData setLength:0];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
NSLog(#"Received %lu bytes of data", [data length]);
[receivedData appendData:data];
NSLog(#"Received data is now %lu bytes", [receivedData length]);
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
NSRunAlertPanel(#"Error",[NSString stringWithFormat:#"Could not connect to server.Following error occured:\n\n%#", error], nil, nil, nil);
NSLog(#"Error receiving response: %#", error);
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
// Once this method is invoked, "responseData" contains the complete result
NSLog(#"Succeeded! Received %lu bytes of data", [receivedData length]);
NSString *dataStr=[[NSString alloc] initWithData:receivedData encoding:NSASCIIStringEncoding] ;
retStr = [NSString stringWithString:dataStr];
finished =TRUE;
// [self returnDcString:dataStr];
// NSLog(#"%#",dataStr);
if ([delegate respondsToSelector:#selector(didFinishDownload:)]) {
NSLog(#"Calling the delegate");
//NSString* dataAsString = [[[NSString alloc] initWithData:receivedData encoding:NSUTF8StringEncoding] autorelease];
// [delegate performSelector:#selector(didFinishDownload:) withObject: dataStr];
}
}
- (void)setDelegate:(id)val
{
delegate = val;
}
- (id)delegate
{
return delegate;
}
#end

Request to web service over SSL

i am struggling to request web service using https. I am getting this kind of error:
An error occured : The certificate for this server is invalid. You might be connecting to a server that is pretending to be “SERVER_ADDRESS” which could put your confidential information at risk.
I am not using NSUrlConnectionDelegate. Here is my method:
- (void)sendRequestWithUrl:(NSURL*)url block:(void (^)(NSDictionary *dict, NSError *error)) block{
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
[request setHTTPMethod:#"POST"];
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
NSError* err = nil;
NSHTTPURLResponse* rsp = nil;
// Perform the request synchronously on this thread
NSData *rspData = [NSURLConnection sendSynchronousRequest:request returningResponse:&rsp error:&err];
if (rspData && err == nil) {
NSDictionary *result = [NSJSONSerialization JSONObjectWithData:rspData options:NSJSONReadingMutableLeaves error:&err];
if(result) {
block(result, err);
} else {
block(nil, err);
}
}else{
DLog(#"Requesting URL: %# An error occured : %#",url,[err localizedDescription]);
block(nil, err);
}
}
How i could solve this problem ?
Apple has a technote that covers this very well:
"Technical Note TN2232: HTTPS Server Trust Evaluation"
https://developer.apple.com/library/ios/#technotes/tn2232/_index.html
Well my solution was to create class AsyncURLConnection this code i found somewhere at http://stackoverflow.com but can't find it now. So i will give the code:
AsyncURLConnection:
.h
#import <Foundation/Foundation.h>
typedef void (^completeBlock_t)(NSData *data);
typedef void (^errorBlock_t)(NSError *error);
#interface AsyncURLConnection : NSObject{
NSMutableData *data_;
completeBlock_t completeBlock_;
errorBlock_t errorBlock_;
}
+ (id)request:(NSString *)requestUrl completeBlock:(completeBlock_t)completeBlock errorBlock:(errorBlock_t)errorBlock;
- (id)initWithRequest:(NSString *)requestUrl completeBlock:(completeBlock_t)completeBlock errorBlock:(errorBlock_t)errorBlock;
+ (id)requestWithMutable:(NSMutableURLRequest *)request completeBlock:(completeBlock_t)completeBlock errorBlock:(errorBlock_t)errorBlock;
- (id)initWithMutableRequest:(NSMutableURLRequest *)request completeBlock:(completeBlock_t)completeBlock errorBlock:(errorBlock_t)errorBlock;
#end
.m
#import "AsyncURLConnection.h"
#implementation AsyncURLConnection
+ (id)requestWithMutable:(NSMutableURLRequest *)request completeBlock:(completeBlock_t)completeBlock errorBlock:(errorBlock_t)errorBlock
{
return [[self alloc] initWithMutableRequest:request completeBlock:completeBlock errorBlock:errorBlock];
}
+ (id)request:(NSString*)requestUrl completeBlock:(completeBlock_t)completeBlock errorBlock:(errorBlock_t)errorBlock
{
return [[self alloc] initWithRequest:requestUrl
completeBlock:completeBlock errorBlock:errorBlock];
}
- (id)initWithRequest:(NSString *)requestUrl completeBlock:(completeBlock_t)completeBlock errorBlock:(errorBlock_t)errorBlock
{
if ((self=[super init])) {
data_ = [[NSMutableData alloc] init];
completeBlock_ = [completeBlock copy];
errorBlock_ = [errorBlock copy];
NSURL *url = [NSURL URLWithString:requestUrl];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
[NSURLConnection connectionWithRequest:request delegate:self];
}
return self;
}
- (id)initWithMutableRequest:(NSMutableURLRequest *)request completeBlock:(completeBlock_t)completeBlock errorBlock:(errorBlock_t)errorBlock
{
if ((self=[super init])) {
data_ = [[NSMutableData alloc] init];
completeBlock_ = [completeBlock copy];
errorBlock_ = [errorBlock copy];
[NSURLConnection connectionWithRequest:request delegate:self];
}
return self;
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
[data_ setLength:0];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
[data_ appendData:data];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
completeBlock_(data_);
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
errorBlock_(error);
}
- (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge {
if ([challenge previousFailureCount] == 0)
{
NSURLCredential *newCredential;
newCredential=[NSURLCredential credentialWithUser:#"someUser"
password:#"someUser"
persistence:NSURLCredentialPersistenceForSession];
[[challenge sender] useCredential:newCredential forAuthenticationChallenge:challenge];
DLog(#"responded to authentication challenge");
}else{
DLog(#"previous authentication failure");
}
}
So in some controller this class could be used:
+ (void) downloadFileForURL:(NSURL *) url completionBlock:(void (^)(NSData *data, NSError *error)) block {
NSString *requestURL = #"https://www.google.lt/restServer/Method";
[AsyncURLConnection request:requestURL completeBlock:^(NSData *data) {
/* success! */
dispatch_queue_t downloadQueue = dispatch_queue_create("Download queue", NULL);
dispatch_async(downloadQueue, ^{
/* process downloaded data in Concurrent Queue */
if (data != nil) {
block(data, nil);
}else{
block(nil,nil);
}
dispatch_async(dispatch_get_main_queue(), ^{
/* update UI on Main Thread */
});
});
} errorBlock:^(NSError *error) {
/* error! */
block(nil,error);
}];
}
Hope some one this code could help.
Thank you all for answers.
You should add below delegate methods to your communication class.
- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
{
if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust])
{
if ([YOUR_HOST isEqualToString:challenge.protectionSpace.host])
{
[challenge.sender useCredential:[NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust] forAuthenticationChallenge:challenge];
}
}
[challenge.sender continueWithoutCredentialForAuthenticationChallenge:challenge];
}
- (BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace
{
return [protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust];
}
This always happen for self-signed certificate. By using NSURLConnectionDelegate
- (BOOL)connection:(NSURLConnection *)connection
canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace
{
if ([protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
return YES;
}
}
Trying this delegate method solved same issue for me.
- (BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace {
return [protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust];
}
- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge {
[challenge.sender useCredential:[NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust] forAuthenticationChallenge:challenge];
[challenge.sender continueWithoutCredentialForAuthenticationChallenge:challenge];
}

Resources