Skip to content

Instantly share code, notes, and snippets.

@roothybrid7
Created January 20, 2013 13:44
Show Gist options
  • Save roothybrid7/4578727 to your computer and use it in GitHub Desktop.
Save roothybrid7/4578727 to your computer and use it in GitHub Desktop.
iOSのUIWebViewでHTTPステータスをチェックする ref: http://qiita.com/items/6e1399cec9fbe865b132
#import <Foundation/Foundation.h>
@interface MyURLProtocol : NSURLProtocol
@end
#import "MyURLProtocol.h"
# ループを防ぐためにHTTPリクエストヘッダーに追加する
static NSString *const MyWebViewResponseCheckHeader = @"X-iOS-WebView-Response-Check";
@interface MyURLProtocol ()
@property (strong, nonatomic, readwrite) NSURLRequest *request;
@end
@implementation MyURLProtocol
/*!
クラスロード時に、URL Loading Systemに登録してしまう
UIWebView/NSURLConnectionなどの通信は、このクラスを利用するようになる
*/
+ (void)load
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
[NSURLProtocol registerClass:[self class]];
});
}
/*!
HTTPステータスをチェックするアプリのホスト名を渡す
@result アプリホスト名
*/
+ (NSString *)appHost
{
__strong static NSString *host = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
host = [[NSURL URLWithString:APP_URL_BASE] host];
});
return host;
}
#pragra mark - URL Path judgment.
/*!
URLProtocol内でチェック対象のホスト
*/
+ (NSArray *)canInitHost
{
__strong static NSArray *hosts = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
hosts = @[[self appHost]];
});
return hosts;
}
/*!
URLProtocol内でチェック対象のパス
*/
+ (NSArray *)cannotInitPathComponentsForHost:(NSString *)host
{
__strong static NSDictionary *pathComponentsDict = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
pathComponentsDict = @{
[self appHost]: @[@"api"],
};
});
return pathComponentsDict[host];
}
#pragra mark - URLProtocol.
/*!
WebViewでページ遷移するものだけフィルターしてNSURLProtocolで処理する
UIWebViewのリクエストのみ処理する
css/js/画像(data:base64;も含む)/APIリクエストは処理しない
*/
+ (BOOL)canInitWithRequest:(NSURLRequest *)request
{
NSLog(@"url: %@", request.URL);
NSLog(@"requesth headers: %@", request.allHTTPHeaderFields);
NSString *scheme = request.URL.scheme;
NSString *pathExtension = request.URL.pathExtension;
NSArray *pathComponents = request.URL.pathComponents;
// MARK: NSURLProtocolで処理済みならスルー
if ([request valueForHTTPHeaderField:MyWebViewResponseCheckHeader]) {
return NO;
}
// MARK: schme=http + extension=''なら画面遷移かAPIリクエスト
if ([scheme isEqualToString:@"http"]
&& [[self canInitHost] containsObject:request.URL.host]
&& pathExtension.length <= 0) {
// MARK: APIリクエストは、WebViewを使わずにリクエストしていてステータス取得可能なのでスルー
if ([[self cannotInitPathComponentsForHost:request.URL.host] firstObjectCommonWithArray:pathComponents]) {
return NO;
}
// Web request
return YES;
}
return NO;
}
+ (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request
{
return request;
}
- (id)initWithRequest:(NSURLRequest *)request cachedResponse:(NSCachedURLResponse *)cachedResponse client:(id<NSURLProtocolClient>)client
{
self = [super initWithRequest:request cachedResponse:cachedResponse client:client];
if (self) {
NSMutableURLRequest *mutableRequest = [request mutableCopy];
// Loop guard.
[mutableRequest setValue:@"1" forHTTPHeaderField:MyWebViewResponseCheckHeader];
self.request = (NSURLRequest *)mutableRequest;
}
return self;
}
- (void)startLoading
{
NSLog(@"request: %@", self.request);
NSLog(@"request.headers: %@", self.request.allHTTPHeaderFields);
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[NSURLConnection
sendAsynchronousRequest:self.request
queue:queue
completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
if (error) {
NSLog(@"reponse error: %@", error);
// NSURLProtocolClientのdidFailWithErrorを呼び出すと
// UIWebViewDelegateのwebView:didFailLoadWithError:が呼ばれる(こっちはいつもどおり)
[self.client URLProtocol:self didFailWithError:error];
return;
}
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
// MARK: UIWebViewの代わりにステータスコードをチェックする
if (httpResponse.statusCode >= 400) {
NSLog(@"status error: %d", httpResponse.statusCode);
// 独自エラーオブジェクトを作成して、NSURLProtocolClientのdidFailWithErrorを呼び出す
// 同じくUIWebViewDelegateのwebView:didFailLoadWithError:が呼ばれる
NSDictionary *userInfo = @{NSLocalizedDescriptionKey: @"エラーっぽい"};
NSError *httpStatusError = [NSError errorWithDomain:MyErrorDomain code:MyHTTPResponseError userInfo:userInfo];
[self.client URLProtocol:self didFailWithError:httpStatusError];
return;
}
// 正常終了
[self.client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageAllowed];
[self.client URLProtocol:self didLoadData:data];
[self.client URLProtocolDidFinishLoading:self];
}];
}
- (void)stopLoading
{
NSLog(@"request: %@", self.request);
NSLog(@"request.headers: %@", self.request.allHTTPHeaderFields);
}
@end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment