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/

 

转载于:https://www.cnblogs.com/mumoozhu/p/4595671.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值