本文系
Smallfan(程序猿小风扇)
原创内容,转载请在文章开头显眼处注明作者和出处。
背景
笔者在
《WKWebView》
一文中提到过,
WKWebView
在独立于 app 进程之外的进程中执行网络请求,请求数据不经过主进程,因此,在
WKWebView
上直接使用 NSURLProtocol 无法拦截请求。所以如果需要使用到拦截请求,有种可行地方案是使用苹果开源的 Webkit2 源码暴露的私有API(详见原文第3小节:NSURLProtocol问题)。
但使用私有API,必然带来以下几个问题:
由此看来,在 iOS11
WKURLSchemeHandler
[
探究
] 到来之前,私有API并不那么完美。
所幸通过寻找发现,iOS系统上具备搭建服务器能力,理论上对实现
WKWebView 离线资源加载
存在可能性。
分析
基于iOS的local web server,目前大致有以下几种较为完善的框架:
因为目前大部分APP已经支持ATS,且国内大部分项目代码仍采用OC实现,故本文将以
CocoaHttpSever
为基础进行实验。
Telegraph
是为补充 CocoaHttpSever 及 GCDWebServer 不足而诞生,对于纯Swift项目,推荐使用
Telegraph
。
初出茅驴
在 project工程文件 中引入 CocoaHttpServer 之后,
-
首先实现一个服务管理。
#import "LocalWebServerManager.h"
#import "HTTPServer.h"
#import "MyHTTPConnection.h"
@interface LocalWebServerManager ()
{
HTTPServer *_httpServer;
}
@end
@implementation LocalWebServerManager
+ (instancetype)sharedInstance {
static LocalWebServerManager *_sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_sharedInstance = [[LocalWebServerManager alloc] init];
});
return _sharedInstance;
}
- (void)start {
_port = 60000;
if (!_httpServer) {
_httpServer = [[HTTPServer alloc] init];
[_httpServer setType:@"_http._tcp."];
[_httpServer setPort:_port];
NSString * webLocalPath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:@"Resource"];
[_httpServer setDocumentRoot:webLocalPath];
NSLog(@"Setting document root: %@", webLocalPath);
}
if (_httpServer && ![_httpServer isRunning]) {
NSError *error;
if([_httpServer start:&error]) {
NSLog(@"start server success in port %d %@", [_httpServer listeningPort], [_httpServer publishedName]);
} else {
NSLog(@"启动失败");
}
}
}
- (void)stop {
if (_httpServer && [_httpServer isRunning]) {
[_httpServer stop];
}
}
@end
-
然后选择其启动时机,一般选择在 AppDelegate 中或 WKWebView 请求之前。
- (void)viewDidLoad {
[super viewDidLoad];
//Setup WKWebView
WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc] init];
WKUserContentController *controller = [[WKUserContentController alloc] init];
configuration.userContentController = controller;
configuration.processPool = [[WKProcessPool alloc] init];
_wkWebView = [[WKWebView alloc] initWithFrame:self.view.bounds
configuration:configuration];
_wkWebView.navigationDelegate = self;
_wkWebView.UIDelegate = self;
[self.view addSubview:_wkWebView];
//Start local web server
[[LocalWebServerManager sharedInstance] start];
//Local request which use local resource
[self loadLocalRequest];
//Remote request which use local resource
// [self loadRemoteRequest];
}
-
在 project工程 中引入相对资源目录(蓝色文件夹),在该目录中实现一个 index.html 和 hi.js 资源文件
Hello