• Customizing UIWebView requests with NSURLProtocol


    Whether you are creating your own web browser on iOS or just displaying some custom, locally generated HTML,UIWebView can become a source of lots of frustration, mostly related to its (perceived) lack of basic customization options.

    Today, we are going to open the pandora’s box and dive into theFoundation’s URL Loading System to customize every requestUIWebView sends.

    UIWebView uses the NSURLConnection class for every request (actually, the NSURLConnection class was originally written for the first release of Safari, that’s why they are so tied together). And every NSURLConnection request is intercepted and treated accordingly either by the cache or other custom protocol handlers. By creating a custom protocol handler (using NSURLProtocol), we can intercept, match and customize every request sent by UIWebView.

    For today’s example, let’s say that we want our UIWebView on iOS to be seen as a Chrome Desktop browser running on Windows. According toUserAgentString.com the current Chrome user agent is:

    Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/29.0.1547.2 Safari/537.36

    So let’s start by creating a subclass of NSURLProtocol:

    #import <Foundation/Foundation.h>
     
    @interface ChromeBrowserURLProtocol : NSURLProtocol
     
    @end
     
    @interface ChromeBrowserURLProtocol ()
    @property (nonatomic, strong) NSURLConnection *connection;
     
    @end
     
    @implementation ChromeBrowserURLProtocol
     
    + (BOOL)canInitWithRequest:(NSURLRequest *)request
    {
       if ([NSURLProtocol propertyForKey:@“UserAgentSet” inRequest:request] != nil)
          return NO;
     
       return YES;
    }

    Whenever the URL Loading system receives a new request, it queries every available protocol handler to determine who can handle that request. Here we use a helper method from NSURLProtocol, called+propertyForKey:inRequest:. This method queries for a custom property that can be applied on any NSMutableURLRequest. On our example, we look for “UserAgentSet”. If the user agent has been set, pass this along. Otherwise, let’s deal with this.

    + (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request
    {
       return request;
    }
     
    - (void)startLoading
    {
       NSMutableURLRequest *newRequest = [self.request mutableCopy];
     
       // Here we set the User Agent
       [newRequest setValue:@"Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/29.0.1547.2 Safari/537.36 Kifi/1.0f" forHTTPHeaderField:@"User-Agent"];
     
       [NSURLProtocol setProperty:@YES forKey:@"UserAgentSet" inRequest:newRequest];
     
       self.connection = [NSURLConnection connectionWithRequest:newRequest delegate:self];
    }

    On -startLoading, we create a mutableCopy of our request and change the User Agent. We must set the “UserAgentSet” property here with +setProperty:forKey:inRequest:, so we know to pass this request along in the future. After that, we just initiate a new NSURLConnection with the request.

    - (void)stopLoading
    {
       [self.connection cancel];
    }
     
    - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
    {
       [self.client URLProtocol:self didLoadData:data];
    }
     
    - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
    {
       [self.client URLProtocol:self didFailWithError:error];
       self.connection = nil;
    }
     
    - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
    {
       [self.client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageAllowed];
    }
     
    - (void)connectionDidFinishLoading:(NSURLConnection *)connection
    {
       [self.client URLProtocolDidFinishLoading:self];
       self.connection = nil;
    }
     
    @end

    These are the delegates for the NSURLConnection class, and we must pass them along to our inner client object, contained inside NSURLProtocol.

    Then, we must register this protocol on you App Delegate’s -(BOOL)application:didFinishLaunchingWithOptions:

    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
    {
        [NSURLProtocol registerClass:[ChromeBrowserURLProtocol class]];
    ...

    And that’s it!

    Of course this only scratches the surface on what can be done with NSURLProtocol. You can create your own caching mechanism, track redirects (with -connection:willSendRequest:redirectResponse:response:), sign requests, mock HTTP responses for testing and a lot more.

    转自:http://eng.kifi.com/customizing-uiwebview-requests-with-nsurlprotocol/

  • 相关阅读:
    《敏捷开发修炼之道》学习笔记3:交付用户想要的软件
    Photoshop快捷键大集合
    如何制作已编译的HTML帮助文件(即CHM帮助文件)
    根本不存在 DIV + CSS 布局这回事
    可将视频转换成Gif动画的相关软件
    SEO是什么?与spam有什么区别呢?
    视频六大编辑软件大比拼
    陈彤:一个网络编辑的11年
    最近出现的p2psvr.exe恶意程序的解决办法
    使用火狐浏览器Firefox的一些小技巧
  • 原文地址:https://www.cnblogs.com/mumoozhu/p/4595671.html
Copyright © 2020-2023  润新知