ios wkwebview離線化加載h5資源解決方案
思路: 使用NSURLProtocol攔截請(qǐng)求轉(zhuǎn)發(fā)到本地。
1.確認(rèn)離線化需求
部門負(fù)責(zé)的app有一部分使用的線上h5頁(yè),長(zhǎng)期以來(lái)加載略慢...
于是考慮使用離線化加載。
確保[低速網(wǎng)絡(luò)]或[無(wú)網(wǎng)絡(luò)]可網(wǎng)頁(yè)秒開。
2.使用[NSURLProtocol]攔截
區(qū)別于uiwebview wkwebview使用如下方法攔截
@interface ViewController () @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; // 區(qū)別于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"]; } }
# 注冊(cè)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是下載后的臨時(shí)保存路徑,需要將它移動(dòng)到需要保存的位置 [[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]真機(jī)不支持直接加載[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臨時(shí)文件夾 [[NSFileManager defaultManager] copyItemAtURL:[NSURL fileURLWithPath:cacheFilePath] toURL:[NSURL fileURLWithPath:tmpFilePath] error:&saveError]; NSLog(@"Migrate dist to tempory ok"); }
5.轉(zhuǎn)發(fā)請(qǐng)求
如果[/static]開頭 => 則轉(zhuǎn)發(fā)[Request]到本地[.css/.js]資源
如果[index.html]結(jié)尾 => 就直接[Load]本地[index.html] (否則[index.html]可能會(huì)加載失敗)
// // 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]結(jié)尾 => 就直接[Load]本地[index.html] - (void)startLoading { NSMutableURLRequest *mutableReqeust = [[self request] mutableCopy]; // 標(biāo)示改request已經(jīng)處理過(guò)了,防止無(wú)限循環(huán) [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.拼接響應(yīng)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.響應(yīng) [[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 - 拼接響應(yīng)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
結(jié)語(yǔ):
完整[DEMO]請(qǐng)參考: https://github.com/meloalright/wk-proxy
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
IOS 開發(fā)之UIView動(dòng)畫的實(shí)例詳解
這篇文章主要介紹了IOS 開發(fā)之UIView動(dòng)畫的實(shí)例詳解的相關(guān)資料,需要的朋友可以參考下2017-07-07iOS 12+ 中檢測(cè)網(wǎng)絡(luò)訪問(wèn)的方法
這篇文章主要介紹了iOS 12+ 中檢測(cè)網(wǎng)絡(luò)訪問(wèn)的方法,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2019-01-01iOS如何獲取設(shè)備型號(hào)的最新方法總結(jié)
在開發(fā)中,我們經(jīng)常需要獲取設(shè)備的型號(hào)以進(jìn)行數(shù)據(jù)統(tǒng)計(jì)或者做不同的適配。這篇文章主要給大家介紹了關(guān)于iOS如何獲取設(shè)備型號(hào)的最新方法,需要的朋友可以參考下2018-11-11iOS自定義相機(jī)實(shí)現(xiàn)拍照、錄制視頻
這篇文章主要為大家詳細(xì)介紹了iOS自定義相機(jī)實(shí)現(xiàn)拍照、錄制視頻,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-04-04iOS13適配三指撤銷和文案限長(zhǎng)實(shí)例詳解
這篇文章主要為大家介紹了iOS13適配三指撤銷和文案限長(zhǎng)實(shí)例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-01-01IOS LaunchScreen設(shè)置啟動(dòng)圖片與啟動(dòng)頁(yè)停留時(shí)間詳解
這篇文章主要介紹了IOS LaunchScreen設(shè)置啟動(dòng)圖片與啟動(dòng)頁(yè)停留時(shí)間詳解的相關(guān)資料,需要的朋友可以參考下2017-02-02iOS實(shí)現(xiàn)對(duì)不同分辨率設(shè)備的字號(hào)大小適配方法
下面小編就為大家分享一篇iOS實(shí)現(xiàn)對(duì)不同分辨率設(shè)備的字號(hào)大小適配方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-01-01簡(jiǎn)單談?wù)凜ore Animation 動(dòng)畫效果
下面小編就為大家?guī)?lái)一篇簡(jiǎn)單談?wù)凜ore Animation 動(dòng)畫效果。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-06-06iOS 中 使用UITextField格式化銀行卡號(hào)碼的解決方案
今天小編給大家分享ios中使用UITextField格式化銀行卡號(hào)碼的實(shí)現(xiàn)思路詳解,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友參考下2016-12-12