4

カスタム を実装しており、内部ネットワークのデータ タスクで の代わりにNSURLProtocol使用したいと考えています。NSURLSessionNSURLConnection

NSURLSession/のチャレンジ ハンドラの内部実装について興味深い問題に遭遇しましたNSURLSessionTask

- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task
                            didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge 
                              completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler;

ここでは、基本的に 2 つの異なるチャレンジ ハンドラーが提供されます。1 つは、チャレンジを処理するために必要なすべての情報が提供されるブロックですが、情報オプションとほぼ 1 対 1 で対応するメソッドを持つcompletionHandlerレガシーもあります。NSURLAuthenticationChallenge.clientcompletionHandler

私はプロトコルを開発しており、特定の認証チャレンジを URL ローディング システムに渡して呼び出し側 API を実装したいので、NSURLSessionクライアント メソッドを使用する必要があります。

- (void)URLProtocol:(NSURLProtocol *)protocol didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge;

completionHandler私の質問は、 andの内部実装がNSURLAuthenticationChallenge.client同じかどうかです。もしそうなら、URL 読み込みシステムが適切なメソッドを呼び出すことを期待して、デリゲート メソッドで完了ハンドラーの呼び出しをスキップできますNSURLAuthenticationChallenge.clientか?

4

1 に答える 1

7

私自身の質問に答えるには、答えはノーです。さらに、Apple が提供するチャレンジ センダーはプロトコル全体を実装していないため、NSURLAuthenticationChallengeSenderクライアントがチャレンジに応答しようとするとクラッシュします。

*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSCFURLSessionConnection performDefaultHandlingForAuthenticationChallenge:]: unrecognized selector sent to instance 0x7ff06d958410'

私の解決策は、ラッパーを作成することでした:

@interface CPURLSessionChallengeSender : NSObject <NSURLAuthenticationChallengeSender>

- (instancetype)initWithSessionCompletionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler;

@end

@implementation CPURLSessionChallengeSender
{
    void (^_sessionCompletionHandler)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential);
}

- (instancetype)initWithSessionCompletionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler
{
    self = [super init];

    if(self)
    {
        _sessionCompletionHandler = [completionHandler copy];
    }

    return self;
}

- (void)useCredential:(NSURLCredential *)credential forAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
{
    _sessionCompletionHandler(NSURLSessionAuthChallengeUseCredential, credential);
}

- (void)continueWithoutCredentialForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
{
    _sessionCompletionHandler(NSURLSessionAuthChallengeUseCredential, nil);
}

- (void)cancelAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge;
{
    _sessionCompletionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge, nil);
}

- (void)performDefaultHandlingForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
{
    _sessionCompletionHandler(NSURLSessionAuthChallengePerformDefaultHandling, nil);
}

- (void)rejectProtectionSpaceAndContinueWithChallenge:(NSURLAuthenticationChallenge *)challenge
{
    _sessionCompletionHandler(NSURLSessionAuthChallengeRejectProtectionSpace, nil);
}

@end

そして、ラップされた送信者を使用して、チャレンジ オブジェクトを新しいオブジェクトに置き換えます。

NSURLAuthenticationChallenge* challengeWrapper = [[NSURLAuthenticationChallenge alloc] initWithAuthenticationChallenge:challenge sender:[[CPURLSessionChallengeSender alloc] initWithSessionCompletionHandler:completionHandler]];
[self.client URLProtocol:self didReceiveAuthenticationChallenge:challengeWrapper];
于 2014-12-22T14:09:57.210 に答える