iOS UIWebView URL拦截

http://www.cocoachina.com/ios/20150626/12161.htmljavascript

 

本文译者:candeladiao,原文:URL filtering for UIWebView on the iPhone
说明:译者在作app开发时,由于页面的javascript文件比较大致使加载速度很慢,因此想把javascript文件打包在app里,当UIWebView须要加载该脚本时就从app本地读取,但UIWebView并不支持加载本地资源。最后从下文中找到了解决方法,第一次翻译,不免有误,你们多多指教。css

iCab Mobile(一款iOS平台的网页浏览器)要实现一个拦截管理器来过滤页面上的广告及其余东西。它有一个简单的基于URL过滤规则的列表(一般由用户维护),当页面包含的资源(图片、js以及css等),文件的URL存在于规则列表中时,资源就不会被加载。html

但看一下UIWebView类的API,会发现咱们没有办法知道UIWebView正在加载什么资源,更糟的是,当你但愿过滤掉某些资源文件的时候,没有方法能够强制UIWebView不去加载这些文件,java

拦截器看起来貌似没有可能实现。ios

固然仍是有解决方案的,不然这篇文件就没什么卵用。git

正如上面所说,实现拦截器不能靠UIWebView,由于UIWebView没有提供任何有用的API。github

对UIWebView的全部请求,要找到一个能中断全部HTTP 请求的切入点,咱们须要先了解一下Cocoa的URL Loading System,由于UIWebView是使用URL Loading System从web端取数据的。咱们须要的切入点NSURLCache类就是URL Loading System的一部分。虽然目前iOS系统不会在磁盘上缓存任何数据(后面的iOS系统版本或许会有不一样),所以在UIWebView开始加载前,NSURLCache管理的缓存数据一般为空,但UIWebView仍然会检测所请求资源文件是否存在于缓存。因此咱们须要作的只是继承NSURLCache并重载其方法:web

1
- (NSCachedURLResponse*)cachedResponseForRequest:(NSURLRequest*)request

UIWebView请求全部资源时都会调用这个方法。由于咱们只须要在这个方法里判断请求的URL是不是咱们想拦截的。若是是则建立一个没有内容的假response,不然只需调用super方法便可。浏览器

以下是实现细节:缓存

1.继承NSURLCache:

FilteredWebCache.h:

1
2
3
4
@interface FilteredWebCache : NSURLCache
@end

子类的主要代码

FilteredWebCache.m:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#import "FilteredWebCache.h"
#import "FilterManager.h"
@implementation FilteredWebCache
- (NSCachedURLResponse*)cachedResponseForRequest:(NSURLRequest*)request
{
     NSURL *url = [request URL];
     BOOL blockURL = [[FilterMgr sharedFilterMgr] shouldBlockURL:url];
     if  (blockURL) {
         NSURLResponse *response =
               [[NSURLResponse alloc] initWithURL:url
                                         MIMEType:@ "text/plain"
                            expectedContentLength:1
                                 textEncodingName:nil];
         NSCachedURLResponse *cachedResponse =
               [[NSCachedURLResponse alloc] initWithResponse:response
                              data:[NSData dataWithBytes: " "  length:1]];
         [ super  storeCachedResponse:cachedResponse forRequest:request];
         [cachedResponse release];
         [response release];
     }
     return  [ super  cachedResponseForRequest:request];
}
@end

首先判断URL是否需拦截(判断经过FilterManager类实现,类实如今此不列出)。若是须要,建立一个无内容的响应对象并把它存在cache中。有人可能会认为只须要返回假的响应对象就够了,不必缓存它。但这样会因响应对象被系统释放而致使app crash。不知道为什么为会这样,多是iOS的bug(Mac OS X 10.5.x也存在一样问题,而10.4.x及更早的系统上没有问题),也多是URL Loading System内部类之间的依赖所致。因此咱们先缓存响应对象。确保全部响应都是真实存在于缓存中,这也iOS但愿的,最重要的是不会crash.

更新:由于假的响应是以大于0的大小来初始化的,看起来结缓存它也是必要的。

2.建立新的缓存:

接下来须要建立一个新的缓存并告诉iOS系统使用新的缓存代替默认的,这样当URL Loading System检测资源缓存时才会调用上面的代码。这要在任意UIWebView开始加载页面前作,显然应该放在app启动的时候:

1
2
3
4
5
6
7
8
NSString *path = ... // the path to the cache file
NSUInteger discCapacity = 10*1024*1024;
NSUInteger memoryCapacity = 512*1024;
FilteredWebCache *cache =
       [[FilteredWebCache alloc] initWithMemoryCapacity: memoryCapacity
                              diskCapacity: discCapacity diskPath:path];
[NSURLCache setSharedURLCache:cache];
[cache release];

这里须要提供一个缓存存储路径。缓存文件由NSURLCache对象自动生成,咱们无需事先建立文件,但要定义缓存文件所存位置(必须是应用程序“沙盒”内,如“tmp”目录或是“Document”目录)

这就是实现UIWebView基于URL进行请求过滤的全部内容,看起来其实并不复杂

注:若是过滤规则在app运行过程当中会改变,你须要从缓存中删除假的响应。NSURLCache提供了删除方法,因此这不是问题。若是过滤规则不会改变,则无需关心

相关文章
相关标签/搜索