How to use IOS nsurlprotocol

Time:2021-2-19

This article introduces the specific usage of IOS nsurlprotocol, and shares it with you as follows:

Nsurlprotocol definition

These two days in the optimization project, accidentally saw the nsurlprotocol, learn to summarize.

Nsurlprotocol is also one of Apple’s many black magic, which allows you to redefine the behavior of Apple’s URL loading system. There are many classes in the URL loading system used to process URL requests, such as nsurl, nsurlrequest, nsurlconnection and nsurlsession When system uses nsurlrequest to obtain resources, it will create an instance of nsurlprotocol subclass. Nsurlprotocol looks like a protocol, but in fact it is a class, and it must use the subclass of this class, and it needs to be registered.

Application scope of nsurlprotocol

As long as it is implemented by using nsurlconnection or nsurlsession, you can do some custom operations through nsurlprotocol.

1. Custom request and response

2. Network cache processing (H5 offline packet and network picture cache)

3. Redirecting network requests

4. Provide data mocking function for test, and use local data return without network.

5. Filter out some illegal requests

6. Switch test environment quickly

7. Block the image loading request and transfer to loading from local file

8. It can intercept uiwebview, network requests encapsulated based on the system’s nsurlconnection or nsurlsession. Currently, wkwebview cannot be intercepted by nsurlprotocol.

9. When multiple custom nsurlprotocol are registered in the system, the URL loading process will be called in reverse order. When one of the nsurlprotocol intercepts the request, the subsequent nsurlprotocol cannot intercept the request.

Nsurlprotocol customization


#import <Foundation/Foundation.h>
 
@interface CustomURLProtocol : NSURLProtocol
 
@end

To implement the following method

+ canInitWithRequest: 
//Abstract method, subclass gives whether it can meet the request. If the response is yes, it means that your customurlprotocol implements the request.
 
+ canonicalRequestForRequest:
//Abstract method, which can be rewritten to modify the request, such as adding new header information, modifying, modifying URL, etc., and returning the modified request.
 
+ requestIsCacheEquivalent:toRequest:
//Look, it's all caching
 
- startLoading:
//To start downloading, you need to initiate a request in this method. For nsurlconnection, it is to create an nsurlconnection. For nsurlsession, it is to initiate an nsurlsession task. Generally, before downloading, you need to set the request to be downloading to prevent multiple downloads
 
- stopLoading:
//Stop the corresponding request and clear the request connection or task

Using the custom nsurlprotocol class

1. Use in a single uiviewcontroller

//Import custom nsurlprotocol class
#import "CustomURLProtocol.h"
//Add code to intercept network requests in viewdidload
//Registration network request interception
[NSURLProtocol registerClass:[CustomURLProtocol class]];
//Add the code to cancel network interception in viewwill disappear
//Unregister network request interception
[NSURLProtocol unregisterClass:[CustomURLProtocol class]];

2. Intercept all network requests in the entire app

//Register the network interception code directly in the didfinish launching with options in appdelegate
//Register protocol
[NSURLProtocol registerClass:[CustomURLProtocol class]];
Examples of using nsurlprotocol

#define URLProtocolHandledKey @"URLProtocolHandledKey"
 
+ (BOOL)canInitWithRequest:(NSURLRequest *)request{
 //Only HTTP and HTTPS requests are processed
  NSString *scheme = [[request URL] scheme];
  if ( ([scheme caseInsensitiveCompare:@"http"] == NSOrderedSame ||
   [scheme caseInsensitiveCompare:@"https"] == NSOrderedSame)){
    //See if it has been dealt with to prevent infinite loops
    if ([NSURLProtocol propertyForKey:URLProtocolHandledKey inRequest:request]) {
      return NO;
    }
    return YES;
  }
  return NO;
}
 
+ (NSURLRequest *) canonicalRequestForRequest:(NSURLRequest *)request {
   /**You can add first-class information here*/
  NSMutableURLRequest *mutableReqeust = [request mutableCopy];
  mutableReqeust = [self redirectHostInRequset:mutableReqeust];
  return mutableReqeust;
}
 
+(NSMutableURLRequest*)redirectHostInRequset:(NSMutableURLRequest*)request{
  if ([request.URL host].length == 0) {
    return request;
  }
 
  NSString *originUrlString = [request.URL absoluteString];
  NSString *originHostString = [request.URL host];
  NSRange hostRange = [originUrlString rangeOfString:originHostString];
  if (hostRange.location == NSNotFound) {
    return request;
  }
  //Directed to home page
  NSString *ip = @"bohemiao.com";
 
  //Replace domain name
  NSString *urlString = [originUrlString stringByReplacingCharactersInRange:hostRange withString:ip];
  NSURL *url = [NSURL URLWithString:urlString];
  request.URL = url;
 
  return request;
}
 
+ (BOOL)requestIsCacheEquivalent:(NSURLRequest *)a toRequest:(NSURLRequest *)b{
  return [super requestIsCacheEquivalent:a toRequest:b];
}
 
- (void)startLoading{
  NSMutableURLRequest *mutableReqeust = [[self request] mutableCopy];
  //Indicates that the request has been processed to prevent infinite loops
  [NSURLProtocol setProperty:@YES forKey:URLProtocolHandledKey inRequest:mutableReqeust];
  self.connection = [NSURLConnection connectionWithRequest:mutableReqeust delegate:self];
  //It's the same with nsurlsession
}
 
- (void)stopLoading{
  [self.connection cancel];
}
 
- (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];
}

Some of the nsurlprotocolclient methods used above

@protocol NSURLProtocolClient <NSObject>
 
//Request redirection
- (void)URLProtocol:(NSURLProtocol *)protocol wasRedirectedToRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)redirectResponse;
 
//Is the response cache legal
- (void)URLProtocol:(NSURLProtocol *)protocol cachedResponseIsValid:(NSCachedURLResponse *)cachedResponse;
 
//Just received the response message
- (void)URLProtocol:(NSURLProtocol *)protocol didReceiveResponse:(NSURLResponse *)response cacheStoragePolicy:(NSURLCacheStoragePolicy)policy;
 
//Data loaded successfully
- (void)URLProtocol:(NSURLProtocol *)protocol didLoadData:(NSData *)data;
 
//Data loading completed
- (void)URLProtocolDidFinishLoading:(NSURLProtocol *)protocol;
 
//Data loading failed
- (void)URLProtocol:(NSURLProtocol *)protocol didFailWithError:(NSError *)error;
 
//Starts validation for the specified request
- (void)URLProtocol:(NSURLProtocol *)protocol didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge;
 
//Cancels validation for the specified request
- (void)URLProtocol:(NSURLProtocol *)protocol didCancelAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge;
 
@end

The above is the whole content of this article, I hope to help you learn, and I hope you can support developer more.