Created
January 20, 2013 13:44
-
-
Save roothybrid7/4578727 to your computer and use it in GitHub Desktop.
iOSのUIWebViewでHTTPステータスをチェックする ref: http://qiita.com/items/6e1399cec9fbe865b132
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#import <Foundation/Foundation.h> | |
@interface MyURLProtocol : NSURLProtocol | |
@end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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