iOS UIWebView URL interception

iOS UIWebView URL interception

When the translator was developing an app, because the javascript file on the page was large and the loading speed was very slow, he wanted to package the javascript file in the app. When UIWebView needed to load the script, it would read it from the local app, but UIWebView does not support loading local resources. ***I found a solution from the following article. This is my first translation, so there are bound to be errors. Please give me your advice.

iCab Mobile (a web browser for iOS) implements an interception manager to filter ads and other things on the page. It has a simple list of URL-based filtering rules (usually maintained by the user), and when the page contains resources (pictures, js, css, etc.), the file URL exists in the rule list, the resource will not be loaded.

But if you look at the API of the UIWebView class, you will find that we have no way to know what resources UIWebView is loading. What's worse, when you want to filter out certain resource files, there is no way to force UIWebView not to load these files.

Interceptors seemed impossible to implement.

Of course there is a solution, otherwise this document would be of little use.

As mentioned above, the implementation of interceptors cannot rely on UIWebView because UIWebView does not provide any useful APIs.

To find an entry point that can interrupt all HTTP requests for all UIWebView requests, we need to first understand Cocoa's URL Loading System, because UIWebView uses the URL Loading System to get data from the web. The entry point we need, the NSURLCache class, is part of the URL Loading System. Although the current iOS system does not cache any data on disk (later iOS system versions may be different), the cached data managed by NSURLCache is usually empty before UIWebView starts loading, but UIWebView will still detect whether the requested resource file exists in the cache. So all we need to do is inherit NSURLCache and overload its methods:

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

UIWebView calls this method when requesting all resources. Because we only need to determine in this method whether the requested URL is the one we want to intercept. If it is, create a fake response with no content, otherwise just call the super method.

The following are the implementation details:

1. Inherit NSURLCache:

FilteredWebCache.h:

  1. @interface FilteredWebCache : NSURLCache
  2. {
  3. }
  4. @end  

Main code of the subclass

FilteredWebCache.m:

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

First, determine whether the URL needs to be intercepted (the judgment is implemented by the FilterManager class, and the class implementation is not listed here). If necessary, create a response object with no content and store it in the cache. Some people may think that it is enough to just return a fake response object and there is no need to cache it. However, this will cause the app to crash because the response object is released by the system. I don’t know why this happens. It may be an iOS bug (Mac OS X 10.5.x also has the same problem, but there is no problem on 10.4.x and earlier systems), or it may be caused by the dependency between the internal classes of the URL Loading System. So we cache the response object first. Make sure that all responses are actually in the cache, which is what iOS wants, and the most important thing is that there will be no crash.

Update: Since the fake response is initialized with a size greater than 0, it seems necessary to cache it as well.

2. Create a new cache:

Next, you need to create a new cache and tell the iOS system to use the new cache instead of the default one, so that the above code will be called when the URL Loading System detects the resource cache. This should be done before any UIWebView starts loading pages, and obviously should be done when the app starts:

  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];

Here you need to provide a cache storage path. The cache file is automatically generated by the NSURLCache object. We do not need to create the file in advance, but we need to define the location where the cache file is stored (it must be in the application "sandbox", such as the "tmp" directory or the "Document" directory)

This is all the content for implementing UIWebView's request filtering based on URL. It doesn't seem complicated.

Note: If the filtering rules change while the app is running, you will need to remove the fake responses from the cache. NSURLCache provides a delete method, so this is not a problem. If the filtering rules do not change, you don't need to worry about it.

<<:  11 great websites for learning iOS development

>>:  Experts talk: Must-read experience sharing for Java programmers switching to Android development

Recommend

How to operate corporate Douyin, how to operate a company Douyin account?

We use TikTok to let more people know your brand,...

Samsung and Huawei tease iPhone 6: Fear is behind it

With the release of Apple iPhone 6 and iPhone 6 P...

Is it my hair that I'm losing? No, it's probably diamonds!

“A diamond is forever.” Diamonds are highly sough...

WeChat red envelope cover is open to individuals, 1 yuan each

According to official news from the WeChat team o...

If the Eastern Zhou Dynasty was a class (final chapter)

Today's article is the final chapter of the E...

Apple's latest data shows that iOS 8 penetration rate is close to 60%

Data from Apple's developer support page show...

A 14-year-old girl performed “sheep face recognition”?

What are you most worried about when playing game...