iOS UIWebView URL拦截

移动开发 iOS
译者在做app开发时,因为页面的javascript文件比较大导致加载速度很慢,所以想把javascript文件打包在app里,当UIWebView需要加载该脚本时就从app本地读取,但UIWebView并不支持加载本地资源。

译者在做app开发时,因为页面的javascript文件比较大导致加载速度很慢,所以想把javascript文件打包在app里,当UIWebView需要加载该脚本时就从app本地读取,但UIWebView并不支持加载本地资源。***从下文中找到了解决方法,***次翻译,难免有误,大家多多指教。

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

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

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

当然还是有解决方案的,否则这篇文件就没什么卵用。

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

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

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

UIWebView请求所有资源时都会调用这个方法。因为我们只需要在这个方法里判断请求的URL是否是我们想拦截的。如果是则创建一个没有内容的假response,否则只需调用super方法即可。

如下是实现细节:

1.继承NSURLCache:

FilteredWebCache.h:

  1. @interface FilteredWebCache : NSURLCache 
  2. @end 

子类的主要代码

FilteredWebCache.m:

  1. #import "FilteredWebCache.h" 
  2. #import "FilterManager.h" 
  3. @implementation FilteredWebCache 
  4. - (NSCachedURLResponse*)cachedResponseForRequest:(NSURLRequest*)request 
  5. NSURL *url = [request URL]; 
  6. BOOL blockURL = [[FilterMgr sharedFilterMgr] shouldBlockURL:url]; 
  7. if (blockURL) { 
  8. NSURLResponse *response = 
  9. [[NSURLResponse alloc] initWithURL:url 
  10. MIMEType:@"text/plain" 
  11. expectedContentLength:1 
  12. textEncodingName:nil]; 
  13. NSCachedURLResponse *cachedResponse = 
  14. [[NSCachedURLResponse alloc] initWithResponse:response 
  15. data:[NSData dataWithBytes:" " length:1]]; 
  16. [super storeCachedResponse:cachedResponse forRequest:request]; 
  17. [cachedResponse release]; 
  18. [response release]; 
  19. return [super cachedResponseForRequest:request]; 
  20. @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. NSString *path = ...// the path to the cache file 
  2. NSUInteger discCapacity = 10*1024*1024
  3. NSUInteger memoryCapacity = 512*1024
  4. FilteredWebCache *cache = 
  5. [[FilteredWebCache alloc] initWithMemoryCapacity: memoryCapacity 
  6. diskCapacity: discCapacity diskPath:path]; 
  7. [NSURLCache setSharedURLCache:cache]; 
  8. [cache release]; 

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

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

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

责任编辑:chenqingxiang 来源: CocoaChina
相关推荐

2011-08-02 11:07:42

iOS开发 UIWebView

2011-08-08 18:26:52

UIWebView图片

2013-03-26 09:24:57

iOS内置URL sc

2011-08-08 17:59:22

UIWebView网络网页

2012-04-23 11:00:56

iOS开发Objective-CJavaScript

2015-08-17 09:46:15

UIjs

2012-06-01 11:02:33

2011-08-17 10:09:25

iPhone开发UIWebViewTouch事件

2015-10-09 09:24:08

2015-06-11 09:52:40

iOS 9苹果WWDC

2016-09-18 20:11:16

IOS10iPhone苹果

2012-04-20 10:39:04

UppsiteiOSAndroid

2016-09-23 20:30:54

Javascriptuiwebview富文本编辑器

2011-07-21 17:00:59

iPhone UIWebView Status Cod

2016-11-16 00:39:39

2015-08-31 11:10:52

ios9内容拦截器

2014-06-27 09:34:03

AngularJS

2013-11-04 09:35:38

Firefox插件拦截FLASH

2015-10-10 09:45:29

2013-12-26 10:13:40

点赞
收藏

51CTO技术栈公众号