专栏名称: Cocoa开发者社区
CocoaChina苹果开发中文社区官方微信,提供教程资源、app推广营销、招聘、外包及培训信息、各类沙龙交流活动以及更多开发者服务。
目录
相关文章推荐
花果科技  ·  iOS 18.4 ... ·  昨天  
花果科技  ·  iOS 18.4 ... ·  昨天  
51好读  ›  专栏  ›  Cocoa开发者社区

基于 LocalWebServer 实现 WKWebView 离线资源加载

Cocoa开发者社区  · 公众号  · ios  · 2017-08-24 11:20

正文

本文系 Smallfan(程序猿小风扇) 原创内容,转载请在文章开头显眼处注明作者和出处。


背景


笔者在 《WKWebView》 一文中提到过, WKWebView 在独立于 app 进程之外的进程中执行网络请求,请求数据不经过主进程,因此,在 WKWebView 上直接使用 NSURLProtocol 无法拦截请求。所以如果需要使用到拦截请求,有种可行地方案是使用苹果开源的 Webkit2 源码暴露的私有API(详见原文第3小节:NSURLProtocol问题)。

但使用私有API,必然带来以下几个问题:


  • 审核风险

  • 拦截http/https时,post请求body丢失

  • 如使用ajax hook方式,可能存在 post header字符长度限制 Put类型请求异常



由此看来,在 iOS11 WKURLSchemeHandler [ 探究 ] 到来之前,私有API并不那么完美。


所幸通过寻找发现,iOS系统上具备搭建服务器能力,理论上对实现 WKWebView 离线资源加载 存在可能性。


分析


基于iOS的local web server,目前大致有以下几种较为完善的框架:



因为目前大部分APP已经支持ATS,且国内大部分项目代码仍采用OC实现,故本文将以 CocoaHttpSever 为基础进行实验。

Telegraph 是为补充 CocoaHttpSever 及 GCDWebServer 不足而诞生,对于纯Swift项目,推荐使用 Telegraph


初出茅驴


在 project工程文件 中引入 CocoaHttpServer 之后,


  1. 首先实现一个服务管理。


#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


  1. 然后选择其启动时机,一般选择在 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];

}


  1. 在 project工程 中引入相对资源目录(蓝色文件夹),在该目录中实现一个 index.html 和 hi.js 资源文件

Hello







请到「今天看啥」查看全文