2017-01-05 2 views
7

J'ai un NSURLProtocol personnalisé ("UrlProtocol") écrit pour intercepter les demandes d'un UIWebView lors de la navigation vers des sites Web spécifiques, et d'appliquer un en-tête HTTP supplémentaire avant d'être envoyé. J'ai suivi https://www.raywenderlich.com/59982/nsurlprotocol-tutorial pour une classe ouvrière. Mon problème vient avec le passage de NSURLConnection obsolète à NSURLSession: J'ai testé une page html d'un fichier extrêmement simple, qui a été chargée avec succès. Cependant, les sites un peu plus complexes avec des ressources telles que les fichiers js, les images, etc. seront temporisés, tandis que NSURLConnection l'ensemble du site se chargera en quelques secondes.Personnalisé NSURLProtocol plus lent après le passage de NSURLConnection à NSURLSession

Je vais coller l'original UrlProtocol en utilisant NSURLConnection, puis la nouvelle classe en utilisant NSURLSession. L'original:

#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 

Le nouveau UrlProtocol en utilisant NSURLSessionDataTasks pour chaque demande:

#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 » est un singleton où j'ai initialisé un NSURLSession destiné à être utilisé tout au long de l'exécution de l'application. Il contient également le jeton que je mets en en-tête HTTP personnalisé pour toutes les demandes:

#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 

Répondre

4

a résolu mon problème en créant une nouvelle valeur par défaut pour chaque NSURLSession NSURLSessionDataTask. Quelque chose n'allait pas avec la façon dont j'essayais de partager une NSURLSession pour toutes mes tâches. La méthode startLoading de URLProtocol est maintenant comme suit:

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

Le simple test de la page HTML avant doit avoir travaillé parce qu'une seule tâche était nécessaire pour charger la page