Using NSURLSession inside NSURLProtocol

2019-06-14 08:34发布

问题:

I'm trying to create a transparent NSURLProtocol for http:// and https:// connections using NSURLSession. However at the moment, even though the completion handler is being run, URL requests with the app (UIWebView) are coming back blank. Does anybody have any ideas? Code is below:

#import "MyURLProtocol.h"

// AppDelegate
#import "AppDelegate.h"

static NSString * const MyURLProtocolHandledKey = @"MyURLProtocolHandledKey";

@interface MyURLProtocol () <NSURLConnectionDelegate,NSURLSessionDelegate>

@property (nonatomic, strong) NSURLConnection *connection;
@property (nonatomic, strong) NSMutableData *mutableData;
@property (nonatomic, strong) NSURLResponse *response;

@end

@implementation MyURLProtocol

+(BOOL)canInitWithRequest:(NSURLRequest*)request
{
    if ([NSURLProtocol propertyForKey:MyURLProtocolHandledKey inRequest:request])
        return NO;
    NSString *scheme = request.URL.scheme.lowercaseString;
    return [scheme isEqualToString:@"http"] || [scheme isEqualToString:@"https"];
}

+ (NSURLRequest *) canonicalRequestForRequest:(NSURLRequest *)request {
    return request;
}

-(void)startLoading
{      
    NSMutableURLRequest *newRequest = [self.request mutableCopy];
    [NSURLProtocol setProperty:@YES forKey:@"MyURLProtocolHandledKey" inRequest:newRequest];

    NSURLRequest *request = newRequest;

    NSURLSession *session = [NSURLSession sharedSession];
    NSURLSessionDataTask *task = [session dataTaskWithRequest:request
                                            completionHandler:
                                  ^(NSData *data, NSURLResponse *response, NSError *error) {
                                      if (error != nil) {
                                          NSLog(@"There was an error");
                                      }
                                      NSLog(@"Completiio handler ran");
                                      self.mutableData = [NSMutableData dataWithData:data];
                                      self.response = response;
                                  }];

    [task resume];
}

- (void) stopLoading {

    [self.connection cancel];
    self.mutableData = nil;
}

// Delegate stuff

- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
    [self.client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
}

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
    [self.client URLProtocol:self didLoadData:data];
}

- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
    [self.client URLProtocolDidFinishLoading:self];
}

- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
    [self.client URLProtocol:self didFailWithError:error];
}

@end

回答1:

Your code is using NSURLConnection delegates to pass data back to the caller, e.g. connectionDidFinishLoading:.

To fix this:

  • Replace these with NSURLSession delegate methods.
  • Create and retain a custom session whose delegate is your protocol class instance; the shared session doesn't have a delegate, so it won't call your class's delegate methods.
  • Remove the callback block so that the delegate method for request completion will be called correctly.