2017-01-05 44 views
7

Mam niestandardowy NSURLProtocol ("UrlProtocol") napisany w celu przechwytywania żądań z UIWebView podczas nawigowania do określonych witryn internetowych i zastosowania dodatkowego nagłówka HTTP przed wysłaniem. Poszłam za https://www.raywenderlich.com/59982/nsurlprotocol-tutorial dla klasy robotniczej. Mój problem polega na zmianie z przestarzałego NSURLConnection na NSURLSession: testowałem wyjątkowo prostą stronę HTML jednego pliku, która została pomyślnie załadowana. Jednak nieco bardziej złożone witryny z zasobami takimi jak pliki js, obrazy itp. Będą działać z limitem czasu, natomiast przy użyciu NSURLConnection cała witryna zostanie załadowana w ciągu kilku sekund.Niestandardowe NSURLProtocol wolniejsze po przełączeniu NSURLConnection na NSURLSession

Wkleję oryginalny UrlProtocol przy użyciu NSURLConnection, a następnie nową klasę przy użyciu NSURLSession. Oryginalny:

#import "UrlProtocol.h" 
#import "Globals.h" 

@implementation UrlProtocol 

+ (BOOL)canInitWithRequest:(NSURLRequest *)request { 
    if (![request.URL.absoluteString hasPrefix:@"http"]) return NO; //No need to intercept non-http requests 

    if ([NSURLProtocol propertyForKey:@"handled" inRequest:request]) { 
     return NO; 
    } 

    return YES; 
} 

+ (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request { 
    NSString* key = @"custom-auth-header"; 
    Globals* globals = [Globals getInstance]; 
    NSString* token = [globals token]; 

    NSMutableURLRequest *newRequest = [request mutableCopy]; //Create a mutable copy that can be modified 
    [newRequest setValue:token forHTTPHeaderField:key]; 

    return [newRequest copy]; //return a non-mutable copy 
} 

+ (BOOL)requestIsCacheEquivalent:(NSURLRequest *)a toRequest:(NSURLRequest *)b { 
    return [super requestIsCacheEquivalent:a toRequest:b]; 
} 

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

    self.connection = [NSURLConnection connectionWithRequest:newRequest delegate:self]; 
} 

- (void)stopLoading { 
    NSLog(@"stopLoading"); 
    [self.connection cancel]; 
    self.connection = nil; 
} 

- (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 

Nowy UrlProtocol użyciu NSURLSessionDataTasks dla każdego żądania:

#import "UrlProtocol.h" 
#import "Globals.h" 

@implementation UrlProtocol 

+ (BOOL)canInitWithRequest:(NSURLRequest * _Nonnull) request { 
    if (![request.URL.absoluteString hasPrefix:@"http"]) return NO; //No need to intercept non-http requests 

    if ([NSURLProtocol propertyForKey:@"handled" inRequest:request]) { 
     return NO; 
    } 

    return YES; 
} 

+ (NSURLRequest * _Nonnull)canonicalRequestForRequest:(NSURLRequest * _Nonnull)request { 
    NSString* key = @"custom-auth-header"; 
    Globals* globals = [Globals getInstance]; 
    NSString* token = [globals token]; 

    NSMutableURLRequest *newRequest = [request mutableCopy]; //Create a mutable copy that can be modified 
    [newRequest setValue:token forHTTPHeaderField:key]; 
    return [newRequest copy]; //return a non-mutable copy 
} 

+ (BOOL)requestIsCacheEquivalent:(NSURLRequest * _Nonnull)a toRequest:(NSURLRequest * _Nonnull)b { 
    return [super requestIsCacheEquivalent:a toRequest:b]; 
} 

- (void)startLoading { 
    NSMutableURLRequest *newRequest = [self.request mutableCopy]; 
    [NSURLProtocol setProperty:@YES forKey:@"handled" inRequest:newRequest]; 
    [Globals setUrlSessionDelegate:self]; 
    Globals* globals = [Globals getInstance]; 
    self.dataTask = [globals.session dataTaskWithRequest:newRequest]; 
    [self.dataTask resume]; 
} 

- (void)URLSession:(NSURLSession * _Nonnull)session dataTask:(NSURLSessionDataTask * _Nullable)dataTask didReceiveData:(NSData * _Nullable)data{ 
    [self.client URLProtocol:self didLoadData:data]; 
} 

- (void)URLSession:(NSURLSession * _Nonnull)session dataTask:(NSURLSessionDataTask * _Nullable)dataTask didReceiveResponse:(NSURLResponse * _Nullable)response 
       completionHandler:(void (^ _Nullable)(NSURLSessionResponseDisposition))completionHandler{ 
    [self.client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed]; 
    completionHandler(NSURLSessionResponseAllow); 
} 

- (void)URLSession:(NSURLSession * _Nonnull)session task:(NSURLSessionTask * _Nonnull)task didCompleteWithError:(NSError * _Nullable)error{ 
    if (error){ 
     [self.client URLProtocol:self didFailWithError:error]; 
    } else { 
     [self.client URLProtocolDidFinishLoading:self]; 
    } 
} 

- (void)URLSession:(NSURLSession * _Nonnull)session task:(NSURLSessionTask * _Nonnull)task willPerformHTTPRedirection:(NSHTTPURLResponse * _Nonnull)response 
         newRequest:(NSURLRequest * _Nonnull)request completionHandler:(void (^ _Nonnull)(NSURLRequest * _Nullable))completionHandler { 
    completionHandler(request); 
} 

- (void)stopLoading { 
    [self.dataTask cancel]; 
    self.dataTask = nil; 
} 

@end 

„Globals” jest pojedyncza gdzie mam zainicjowany jedną NSURLSession ma być używany przez cały czas pracy aplikacji. Zawiera on również token ja ustawić jako niestandardowy nagłówek HTTP dla wszystkich żądań:

#import "Globals.h" 
#import "UrlProtocol.h" 

@implementation Globals 
@synthesize token; 
@synthesize session; 

static Globals *instance = nil; 

+(Globals*) getInstance 
{ 
    @synchronized(self) 
    { 
     if (instance == nil) 
     { 
      instance = [Globals new]; 
     } 
    } 
    return instance; 
} 

//UrlProtocol class has no init method, so the NSURLSession delegate is being set on url load. We will ensure only one NSURLSession is created. 
+(void) setUrlSessionDelegate:(UrlProtocol*) urlProtocol{ 
    Globals* globals = [Globals getInstance]; 
    if (!globals.session){ 
     globals.session = [NSURLSession sessionWithConfiguration:NSURLSessionConfiguration.defaultSessionConfiguration delegate:urlProtocol delegateQueue:nil]; 
    } 
} 

@end 

Odpowiedz

4

rozwiązać mój problem tworząc nową domyślną NSURLSession dla każdego NSURLSessionDataTask. Coś było nie tak ze sposobem, w jaki próbowałem dzielić jedną NSURLSession dla wszystkich moich zadań. Metoda URLProtocol za startLoading przedstawia się następująco:

- (void)startLoading { 
    NSMutableURLRequest *newRequest = [self.request mutableCopy]; 
    [NSURLProtocol setProperty:@YES forKey:@"handled" inRequest:newRequest]; 
    NSURLSessionConfiguration* config = NSURLSessionConfiguration.defaultSessionConfiguration; 
    NSURLSession* session = [NSURLSession sessionWithConfiguration:config delegate:self delegateQueue:nil]; 
    self.dataTask = [session dataTaskWithRequest:newRequest]; 
    [self.dataTask resume]; 
} 

Prosty test strona HTML przed musiało pracował ponieważ jedynym zadaniem było potrzebne, aby załadować stronę