2017-01-05 57 views
7

我有一個自定義的NSURLProtocol(「UrlProtocol」),用於在導航到特定網站時攔截來自UIWebView的請求,並在發送之前應用額外的HTTP標頭。我跟着https://www.raywenderlich.com/59982/nsurlprotocol-tutorial工作。我的問題是從已棄用的NSURLConnection切換到NSURLSession:我測試了一個非常簡單的單文件html頁面,該頁面已成功加載。然而,像js文件,圖像等資源稍微複雜的網站將超時,而使用NSURLConnection整個網站將在幾秒鐘內加載。將NSURLConnection切換到NSURLSession後自定義NSURLProtocol

我將使用NSURLConnection粘貼原始UrlProtocol,然後使用NSURLSession的新類。原:

#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 

UrlProtocol使用NSURLSessionDataTasks爲每一個請求:

#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 

「全局」是我已經初始化一個NSURLSession意味着整個應用程序的運行時要使用的單。它還包含我設置爲所有請求自定義HTTP標頭中的令牌:

#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 

回答

4

通過創建一個新的默認NSURLSession每個NSURLSessionDataTask解決我的問題。我嘗試爲我的所有任務共享一個NSURLSession的方式出錯了。 URLProtocol的startLoading方法如下內容:

- (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]; 
} 

,因爲需要只有一個任務來加載頁面

簡單的HTML頁面測試之前,一定很成功