ios wkwebview离线化加载h5资源解决方案
时间:2019-04-14
本文章向大家介绍ios wkwebview离线化加载h5资源解决方案,主要包括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
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。
- 探寻ASP.NET MVC鲜为人知的奥秘(3):寻找多语言的最佳实践方式
- 探寻ASP.NET MVC鲜为人知的奥秘(2):与Entity Framework配合,让异步贯穿始终
- Canvas 剪切图片
- 探寻ASP.NET MVC鲜为人知的奥秘(1):对LESS的支持
- Canvas 图片平铺设置
- Canvas 给图形绘制阴影
- State模式的经典应用场景:订单处理(c#实现)场景描述遇到问题解决问题走起
- ASP.NET SignalR 2.0入门指南介绍SignalRSignalR和WebSocket传输和回滚HTML5 传输协议Comet transports传输协议选择过程监测传输指定传输协议连接
- ASP.NET5之客户端开发:Grunt和Gulp构建工具在Visual Studio 2015中的高效的应用Grunt和Gulp使用Grunt准备项目配置NPM配置Grunt集成起来监测文件变化与V
- 有趣的算法(三)——Hash算法
- JavaScript中的数据类型
- Logistic回归算法及Python实现
- <script>元素在XHTML中的用法
- 有趣的算法(四)——一致性Hash算法模拟redis集群
- java教程
- Java快速入门
- Java 开发环境配置
- Java基本语法
- Java 对象和类
- Java 基本数据类型
- Java 变量类型
- Java 修饰符
- Java 运算符
- Java 循环结构
- Java 分支结构
- Java Number类
- Java Character类
- Java String类
- Java StringBuffer和StringBuilder类
- Java 数组
- Java 日期时间
- Java 正则表达式
- Java 方法
- Java 流(Stream)、文件(File)和IO
- Java 异常处理
- Java 继承
- Java 重写(Override)与重载(Overload)
- Java 多态
- Java 抽象类
- Java 封装
- Java 接口
- Java 包(package)
- Java 数据结构
- Java 集合框架
- Java 泛型
- Java 序列化
- Java 网络编程
- Java 发送邮件
- Java 多线程编程
- Java Applet基础
- Java 文档注释
- linux下日志定时轮询的流程详解
- Vim中查找替换及正则表达式的使用详解
- CentOS 7下部署php7.1和开启MySQL扩展的方法教程
- Ubuntu系统下用Crontab命令定时执行PHP文件详解
- CentOS7 LNMP+phpmyadmin环境搭建 第三篇phpmyadmin安装
- Linux有限状态机FSM的理解与实现
- Linux下浅谈crond与crontab的命令用法
- centos 7系统下安装Jenkins的步骤详解
- linux tomcat配置https的方法
- Linux 中firewall的使用方法总结
- CentOS 7 安装vsftpd 服务器的具体操作步骤
- 详细介绍通过配置Apache实现404页面替换
- bug分支和feature分支_动力节点Java学院整理
- Linux下Python脚本自启动与定时任务详解
- Linux服务器tomact 8.0启动慢的完美解决方法