1

According to the documentation of NSURLSessionConfiguration::protocolClasses, there is no guaranty, that my custom url protocol will be used. How can I ensure, that it is used whenever I set it to protocolClasses property?

Prior to handling a request, an NSURLSession object searches the default protocols first and then checks your custom protocols until it finds one capable of handling the specified request. It uses the protocol whose canInitWithRequest: class method returns YES, indicating that the class is capable of handling the specified request.

I can't set an array with single URL protocol, because it has logic for canInitWithRequest: method and might not handle all request.

NSArray *currentProtocolClasses = sessionConfiguration.protocolClasses ?: @[];
NSMutableArray *protocolClasses = [NSMutableArray arrayWithArray:currentProtocolClasses];
[protocolClasses insertObject:[CustomURLProtocol class] atIndex:0];
sessionConfiguration.protocolClasses = protocolClasses;
JastinBall
  • 873
  • 8
  • 25

1 Answers1

2

If the docs say that, it's a bug. Please file one. The logic is actually much simpler than what is described there. Basically what the OS does is this:

NSURLProtocol *protocol = nil;
for (Class protocolClass in sessionConfiguration.protocolClasses) {
    if ([protocolClass canInitWithRequest:request]) {
         protocol = [[protocolClass alloc] init];
    }
}
if (!protocol) {
    fail
}

So as long as your protocols are listed first, they'll get priority. (For NSURLConnection, that bit of the doc was also wrong; your registered protocols are always asked first, before any of the built-in protocols.)

If you don't need to handle standard protocols, it is sufficient for you to do this:

sessionConfiguration.protocolClasses = @[[CustomURLProtocol class]];

You cannot force a URL protocol to be used. The protocol will be used if and only if its canInitWithRequest: class method returns YES for the request. If you want to make a different protocol handle a request (e.g. if you want to define a custom URL scheme that really uses a normal https request), then you would typically do so by writing a protocol that rewrites the URL and reissues the request in a new session that does not have your protocol handler class installed.

Alternatively, you can reissue the request in a session that does have your handler installed so long as you modify the request in some way so your protocol handler knows to return NO from its canInitWithRequest: method when it sees the request a second time. (Otherwise, you'll get infinite recursion.)

dgatwood
  • 10,129
  • 1
  • 28
  • 49
  • Hi! Could you, please, change the example code to `sessionConfiguration.protocolClasses = @[[CustomURLProtocol class]];` in order to be more generic? Thanks – JastinBall May 23 '17 at 13:06
  • 1
    Yeah, sure. Done. – dgatwood May 23 '17 at 18:10
  • Regarding "the array will always be empty or nil": I create default sessionConfiguration. Its protocolClasses array already contains 5 OS protocols: `_NSURLHTTPProtocol`, `_NSURLDataProtocol`, `_NSURLFTPProtocol`, `_NSURLFileProtocol`, `NSAboutURLProtocol`. I need to add my `CustomURLProtocol`. Whenever my `CustomURLProtocol` returns NO in `canInitWithRequest:`, the app must fallback to OS protocols. – JastinBall May 23 '17 at 19:53
  • Ah. I guess I'm remembering wrong about that. I'll edit that, then. – dgatwood May 23 '17 at 20:25