Skip to content

Instantly share code, notes, and snippets.

@SQiShER
Last active July 16, 2020 10:51
Show Gist options
  • Save SQiShER/5009086 to your computer and use it in GitHub Desktop.
Save SQiShER/5009086 to your computer and use it in GitHub Desktop.
Here is my solution for a better synchronous NSURLConnection. Although asynchronous NSURLConnections are usually a good way to go, sometimes there is just no way around waiting (and blocking) until a request finished. NSURLConnection offers a method to perform synchronous requests, but it is far from perfect and has some serious flaws. In my cas…
#import <Foundation/Foundation.h>
@interface URLConnection : NSObject <NSURLConnectionDataDelegate, NSURLConnectionDelegate>
+ (NSData *)sendSynchronousRequest:(NSURLRequest *)request
returningResponse:(NSURLResponse **)response
error:(NSError **)error;
@end
#import "URLConnection.h"
@interface URLConnection ()
@property(nonatomic, strong) NSURLConnection *connection;
@property(nonatomic, strong) NSURLResponse *response;
@property(nonatomic, strong) NSData *responseData;
@property(nonatomic, strong) NSCondition *condition;
@property(nonatomic, strong) NSError *error;
@property(nonatomic) BOOL connectionDidFinishLoading;
@end
@implementation URLConnection
+ (NSData *)sendSynchronousRequest:(NSURLRequest *)request
returningResponse:(NSURLResponse **)response
error:(NSError **)error {
return [[[URLConnection alloc] init] sendSynchronousRequest:request returningResponse:response error:error];
}
- (id)init {
self = [super init];
if (self) {
self.condition = [[NSCondition alloc] init];
self.connection = nil;
self.connectionDidFinishLoading = NO;
self.error = nil;
self.response = nil;
self.responseData = [NSData data];
}
return self;
}
- (NSData *)sendSynchronousRequest:(NSURLRequest *)request
returningResponse:(NSURLResponse **)response
error:(NSError **)error {
NSParameterAssert(request);
NSAssert(!self.connection, @"This method may only be called once");
self.connection = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:NO];
[self.connection setDelegateQueue:[[NSOperationQueue alloc] init]];
[self.connection start];
[self waitForConnectionToFinishLoading];
if (self.error != nil) {
if (response) *response = nil;
if (error) *error = self.error;
return nil;
} else {
if (response) *response = self.response;
if (error) *error = nil;
return self.responseData;
}
}
- (void)waitForConnectionToFinishLoading {
[self.condition lock];
while (!self.connectionDidFinishLoading) {
[self.condition wait];
}
[self.condition unlock];
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
self.response = response;
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
NSMutableData *mutableResponse = self.responseData.mutableCopy;
[mutableResponse appendData:data];
self.responseData = mutableResponse.copy;
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
[self.condition lock];
self.error = error;
self.connectionDidFinishLoading = YES;
[self.condition signal];
[self.condition unlock];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
[self.condition lock];
self.connectionDidFinishLoading = YES;
[self.condition signal];
[self.condition unlock];
}
@end
@scascalesageinfoes
Copy link

Hello. I add didReceiveAuthenticationChallenge but not enter. why?:

  • (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge {
    if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodHTTPBasic] || [challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodNTLM]) {
    if ([challenge previousFailureCount] == 0) {
    NSURLCredential *credential = [NSURLCredential credentialWithUser:@"[email protected]"
    password:@"Abril2020.sap"
    persistence:NSURLCredentialPersistenceForSession];
    [[challenge sender] useCredential:credential forAuthenticationChallenge:challenge];
    }
    else
    {
    [[challenge sender] cancelAuthenticationChallenge:challenge];
    // inform the user that the user name and password
    // in the preferences are incorrect
    NSLog (@"failed authentication");
    // ...error will be handled by connection didFailWithError
    self.error = [NSError errorWithDomain:@"" code:500 userInfo:@{@"Error reason": @"failed authentication"}];
    }
    }
    }

Is NSURLAuthenticationMethodNTLM type
Thx

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment