思路: 使用NSURLProtocol攔截請求轉發到本地。
1.確認離線化需求
部門負責的app有一部分使用的線上h5頁,長期以來加載略慢...
于是考慮使用離線化加載。
確保[低速網絡]或[無網絡]可網頁秒開。
2.使用[NSURLProtocol]攔截
區別于uiwebview wkwebview使用如下方法攔截
@interface ViewController ()@end@implementation ViewController- (void)viewDidLoad { [super viewDidLoad]; // 區別于uiwebview wkwebview使用如下方法攔截 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"]; }}
# 注冊NSURLProtocol攔截- (IBAction)regist:(id)sender { [NSURLProtocol registerClass:[FilteredProtocol class]];}
# 注銷NSURLProtocol攔截- (IBAction)unregist:(id)sender { [NSURLProtocol unregisterClass:[FilteredProtocol class]];}
3.下載[zip] + 使用[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]; // 初始化cachepath NSString *cachePath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject]; NSFileManager *fm = [NSFileManager defaultManager]; // 刪除之前已有的文件 [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"]]; // location是下載后的臨時保存路徑,需要將它移動到需要保存的位置 [[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];}
4.遷移資源至[NSTemporary]
[wkwebview]真機不支持直接加載[NSCache]資源
需要先遷移資源至[NSTemporary]
- (void)migrateDistToTempory { NSFileManager *fm = [NSFileManager defaultManager]; NSString *cacheFilePath = [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:@"dist"]; NSString *tmpFilePath = [NSTemporaryDirectory() stringByAppendingPathComponent:@"dist"]; // 先刪除tempory已有的dist資源 [fm removeItemAtPath:tmpFilePath error:nil]; NSError *saveError; // 從caches拷貝dist到tempory臨時文件夾 [[NSFileManager defaultManager] copyItemAtURL:[NSURL fileURLWithPath:cacheFilePath] toURL:[NSURL fileURLWithPath:tmpFilePath] error:&saveError]; NSLog(@"Migrate dist to tempory ok");}
5.轉發請求
如果[/static]開頭 => 則轉發[Request]到本地[.css/.js]資源
如果[index.html]結尾 => 就直接[Load]本地[index.html] (否則[index.html]可能會加載失敗)
//// ProtocolCustom.m// proxy-browser//// Created by melo的微博 on 2018/4/8.// Copyright © 2018年 com. All rights reserved.//#import <objc/runtime.h>#import <Foundation/Foundation.h>#import <MobileCoreServices/MobileCoreServices.h>static NSString*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;@end@implementation FilteredProtocol+ (BOOL)canInitWithRequest:(NSURLRequest *)request{ return [NSURLProtocol propertyForKey:FilteredKey inRequest:request]== nil;}+ (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request{ NSLog(@"Got it request.URL.absoluteString = %@",request.URL.absoluteString); NSMutableURLRequest *mutableReqeust = [request mutableCopy]; //截取重定向 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;}+ (BOOL)requestIsCacheEquivalent:(NSURLRequest *)a toRequest:(NSURLRequest *)b{ return [super requestIsCacheEquivalent:a toRequest:b];}# 如果[index.html]結尾 => 就直接[Load]本地[index.html]- (void)startLoading { NSMutableURLRequest *mutableReqeust = [[self request] mutableCopy]; // 標示改request已經處理過了,防止無限循環 [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]; //3.拼接響應Response NSInteger dataLength = data.length; NSString *mimeType = [self getMIMETypeWithCAPIAtFilePath:path]; NSString *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.響應 [[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]; }}- (void)stopLoading{ if (self.connection != nil) { [self.connection cancel]; self.connection = nil; }}- (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);}#pragma mark - 拼接響應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;}#pragma mark- NSURLConnectionDelegate- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error { [self.client URLProtocol:self didFailWithError:error];}#pragma mark - NSURLConnectionDataDelegate- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response{ self.responseData = [[NSMutableData alloc] init]; [self.client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];}- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data { [self.responseData appendData:data]; [self.client URLProtocol:self didLoadData:data];}- (void)connectionDidFinishLoading:(NSURLConnection *)connection { [self.client URLProtocolDidFinishLoading:self];}+ (NSString *)generateProxyPath:(NSString *) absoluteURL { NSString *tmpFilePath = [NSTemporaryDirectory() stringByAppendingPathComponent:@"dist"]; NSString *fileAbsoluteURL = [@"file:/" stringByAppendingString:tmpFilePath]; return [absoluteURL stringByReplacingOccurrencesOfString:regPrefix withString:fileAbsoluteURL];}+ (NSString *)generateDateReadPath:(NSString *) absoluteURL { NSString *fileDataReadURL = [NSTemporaryDirectory() stringByAppendingPathComponent:@"dist"]; return [absoluteURL stringByReplacingOccurrencesOfString:regPrefix withString:fileDataReadURL];}@end
結語:
完整[DEMO]請參考: https://github.com/meloalright/wk-proxy
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持VEVB武林網。
新聞熱點
疑難解答