NSURLProtocol intercepts the request and forwards it to the local.

This article is quite old and may not be applicable now

1. Confirm offline requirements

Part of the app in charge of the department uses the online H5 page, which has been loading slightly slowly for a long time. Consider using offline loading. Make sure [low speed network] or [no network] can be turned on in seconds.Copy the code

2. Use [NSURLProtocol] for interception

Unlike UIWebView, WKWebView intercepts using the following method

@interface ViewController () @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; / / different from uiwebview wkwebview USES the following methods to intercept Class CLS = NSClassFromString (@ "WKBrowsingContextController"); SEL sel = NSSelectorFromString(@"registerSchemeForCustomProtocol:"); if ([(id)cls respondsToSelector:sel]) { [(id)cls performSelector:sel withObject:@"http"]; [(id)cls performSelector:sel withObject:@"https"]; }}Copy the code
# Register NSURLProtocol interceptor - (IBAction)regist:(id)sender {[NSURLProtocol registerClass:[FilteredProtocol class]]; }Copy the code
# NSURLProtocol Interceptor - (IBAction)unregist:(id)sender {[NSURLProtocol unregisterClass:[FilteredProtocol class]]; }Copy the code

3. Download [zip] and decompress it using [SSZipArchive]

#import "SSZipArchive. H

- (void)downloadZip { NSDictionary *_headers; NSURLSession *_session = [self sessionWithHeaders:_headers]; NSURL * url = [NSURL URLWithString: @ "http://10.2.138.225:3238/dist.zip"); NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; / / initialize cachepath nsstrings * cachepath = [NSSearchPathForDirectoriesInDomains (NSCachesDirectory NSUserDomainMask, YES) lastObject]; NSFileManager *fm = [NSFileManager defaultManager]; / / delete existing files before [FM removeItemAtPath: [cachePath stringByAppendingPathComponent: @ "dist.zip"] error: nil]; NSURLSessionDownloadTask *downloadTask=[_session downloadTaskWithRequest:request completionHandler:^(NSURL *location, NSURLResponse *response, NSError *error) { if (!error) { NSError *saveError; NSURL *saveUrl = [NSURL fileURLWithPath: [cachePath stringByAppendingPathComponent:@"dist.zip"]]; [[NSFileManager defaultManager] copyItemAtURL: Location toURL:saveUrl [NSFileManager defaultManager] copyItemAtURL: Location toURL:saveUrl error:&saveError]; if (!saveError) { NSLog(@"task ok"); if([SSZipArchive unzipFileAtPath: [cachePath stringByAppendingPathComponent:@"dist.zip"] toDestination:cachePath]) { NSLog(@"unzip ok"); } else {NSLog(@"unzip err");} else {NSLog(@"task err");  } } else { NSLog(@"error is :%@", error.localizedDescription); } }]; [downloadTask resume]; }Copy the code

4. Migrating resources to [Nstemtempo]

[WkWebView] [Tempo Rary] Does not support direct loading of [NSCache] resources.

- (void)migrateDistToTempory { NSFileManager *fm = [NSFileManager defaultManager]; NSString *cacheFilePath = [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:@"dist"]; NSString *tmpFilePath = [NSTemporaryDirectory() stringByAppendingPathComponent:@"dist"]; / / first remove tempory existing resources of dist [FM removeItemAtPath: tmpFilePath error: nil]; NSError *saveError; / / copy from caches dist to tempory temporary folder [[NSFileManager defaultManager] copyItemAtURL: [NSURL fileURLWithPath: cacheFilePath] toURL:[NSURL fileURLWithPath:tmpFilePath] error:&saveError]; NSLog(@"Migrate dist to tempory ok"); }Copy the code

5. Forward the request

If [/static] starts => forward [Request] to local [.css/.js] resources. If [index.html] ends => Load local [index.html] resources.

// // protocolcustom. m // proxy-browser // // Created by Melo weibo on 2018/4/8. // Copyright © 2018 com. All Rights reserved. // #import <objc/runtime.h> #import <Foundation/Foundation.h> #import < MobileCoreServices/MobileCoreServices. H > static nsstrings * const matchingPrefix = @ "http://10.2.138.225:3233/static/"; Static NSString*const regPrefix = @"http://10.2.138.225:3233"; static NSString*const FilteredKey = @"FilteredKey"; @interface FilteredProtocol : NSURLProtocol @property (nonatomic, strong) NSMutableData *responseData; @property (nonatomic, strong) NSURLConnection *connection; @endCopy the code
@implementation FilteredProtocol
+ (BOOL)canInitWithRequest:(NSURLRequest *)request
{
    return [NSURLProtocol propertyForKey:FilteredKey inRequest:request]== nil;
}
Copy the code
+ (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request { NSLog(@"Got it request.URL.absoluteString = %@",request.URL.absoluteString); NSMutableURLRequest *mutableReqeust = [request mutableCopy]; / / interception redirect the if ([request. URL. AbsoluteString hasPrefix: matchingPrefix]) {NSURL * proxyURL = [NSURL URLWithString:[FilteredProtocol generateProxyPath: request.URL.absoluteString]]; NSLog(@"Proxy to = %@", proxyURL); mutableReqeust = [NSMutableURLRequest requestWithURL: proxyURL]; } return mutableReqeust; }Copy the code
+ (BOOL)requestIsCacheEquivalent:(NSURLRequest *)a toRequest:(NSURLRequest *)b
{
    return [super requestIsCacheEquivalent:a toRequest:b];
}
Copy the code
HTML - (void)startLoading {NSMutableURLRequest *mutableReqeust = [[self] request] mutableCopy]; [NSURLProtocol setProperty: @yes forKey:FilteredKey inRequest:mutableReqeust]; if ([self.request.URL.absoluteString hasSuffix:@"index.html"]) { NSURL *url = self.request.URL; NSString *path = [FilteredProtocol generateDateReadPath: self.request.URL.absoluteString]; NSLog(@"Read data from path = %@", path); NSFileHandle *file = [NSFileHandle fileHandleForReadingAtPath:path]; NSData *data = [file readDataToEndOfFile]; NSLog(@"Got data = %@", data); [file closeFile]; NSInteger dataLength = data.length; NSString *mimeType = [self getMIMETypeWithCAPIAtFilePath:path]; Nsstrings * httpVersion = @ "HTTP / 1.1"; NSHTTPURLResponse *response = nil; if (dataLength > 0) { response = [self jointResponseWithData:data dataLength:dataLength mimeType:mimeType requestUrl:url  statusCode:200 httpVersion:httpVersion]; } else { response = [self jointResponseWithData:[@"404" dataUsingEncoding:NSUTF8StringEncoding] dataLength:3 mimeType:mimeType requestUrl:url statusCode:404 httpVersion:httpVersion]; } / / 4. Response [[self client] URLProtocol: self didReceiveResponse: response cacheStoragePolicy: NSURLCacheStorageNotAllowed]; [[self client] URLProtocol:self didLoadData:data]; [[self client] URLProtocolDidFinishLoading:self]; } else { self.connection = [NSURLConnection connectionWithRequest:mutableReqeust delegate:self]; }}Copy the code
- (void)stopLoading
{
    if (self.connection != nil)
    {
        [self.connection cancel];
        self.connection = nil;
    }
}
Copy the code
- (NSString *)getMIMETypeWithCAPIAtFilePath:(NSString *)path { if (! [[[NSFileManager alloc] init] fileExistsAtPath:path]) { return nil; } CFStringRef UTI = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, (__bridge CFStringRef)[path pathExtension], NULL); CFStringRef MIMEType = UTTypeCopyPreferredTagWithClass (UTI, kUTTagClassMIMEType); CFRelease(UTI); if (! MIMEType) { return @"application/octet-stream"; } return (__bridge NSString *)(MIMEType); }Copy the code
Response - (NSHTTPURLResponse *)jointResponseWithData:(NSData *)data dataLength:(NSInteger)dataLength  mimeType:(NSString *)mimeType requestUrl:(NSURL *)requestUrl statusCode:(NSInteger)statusCode httpVersion:(NSString *)httpVersion { NSDictionary *dict = @{@"Content-type":mimeType, @"Content-length":[NSString stringWithFormat:@"%ld",dataLength]}; NSHTTPURLResponse *response = [[NSHTTPURLResponse alloc] initWithURL:requestUrl statusCode:statusCode HTTPVersion:httpVersion headerFields:dict]; return response; }Copy the code
#pragma mark- NSURLConnectionDelegate - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error  { [self.client URLProtocol:self didFailWithError:error]; }Copy the code
#pragma mark - NSURLConnectionDataDelegate
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
    self.responseData = [[NSMutableData alloc] init];
    [self.client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
}
Copy the code
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
    [self.responseData appendData:data];
    [self.client URLProtocol:self didLoadData:data];
}
Copy the code
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
    [self.client URLProtocolDidFinishLoading:self];
}
Copy the code
+ (NSString *)generateProxyPath:(NSString *) absoluteURL {
    NSString *tmpFilePath = [NSTemporaryDirectory() stringByAppendingPathComponent:@"dist"];
    NSString *fileAbsoluteURL = [@"file:/" stringByAppendingString:tmpFilePath];
    return [absoluteURL stringByReplacingOccurrencesOfString:regPrefix
                                                 withString:fileAbsoluteURL];
}
Copy the code
+ (NSString *)generateDateReadPath:(NSString *) absoluteURL {
    NSString *fileDataReadURL = [NSTemporaryDirectory() stringByAppendingPathComponent:@"dist"];
    return [absoluteURL stringByReplacingOccurrencesOfString:regPrefix
                                                  withString:fileDataReadURL];
}
@end
Copy the code

Conclusion:

For full [DEMO] please refer to: github.com/meloalright…

(∩_∩) Ask to a ☆ oh

Credit:

Reference Documents:

Github: Yeatse/NSURLProtoc… 3. Shorthand: Get the MIME Type of the file