ios wkwebview离线化加载h5资源解决方案
思路: 使用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
上一篇:iOS开发中使用UILabel设置字体的相关技巧小结
栏 目:iOS代码
下一篇:iOS10 ATS 配置详细介绍
本文标题:ios wkwebview离线化加载h5资源解决方案
本文地址:http://www.codeinn.net/misctech/95965.html