Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

iOS - Use AFNetworking with custom NSURLProtocol class

I have a custom NSURLProtocol class that I have implemented with the help of this tutorial. My implementations are pretty much the same as in the tutorial, aside from the names, data model, etc...

Essentially, I am trying to send a HTTP request, but instead of the URL starting with: "http://", it needs to start with, say: "bla://"

Now, I am trying to register the protocol class and use it via the AFNetworking framework, and I'm having some trouble.

The canInitWithRequest: method starts returning NO at some point, and at this point the request fails and I keep getting a "unsupported URL" error.

In addition to registering the protocol class, I have tried to add the class to AFHTTPSessionManager's protocolClasses by calling this in thedidFinishLaunchingWithOptions method:

[NSURLProtocol registerClass:[MyURLProtocol class]];
NSMutableArray *protocolsArray = [NSMutableArray arrayWithArray:[AFHTTPSessionManager manager].session.configuration.protocolClasses];
[protocolsArray addObject:[MyURLProtocol class]];
[AFHTTPSessionManager manager].session.configuration.protocolClasses = [protocolsArray copy];

And I have also added the url scheme to the URL Schemes field in the app's info.plist

Still no luck... Is what I'm trying to do even possible? And if so, what could I be missing? Thanks

like image 625
orenk86 Avatar asked Jan 26 '15 10:01

orenk86


3 Answers

So, for other looking for information about this:

Along with the fact that AFURLSessionManager doesn't use the standard NSURLProtocol registrations, it also processes the array First-In-First-Out, not Last-In-First-Out like NSURLProtocol.

Meaning, if you want to overwrite the behavior of the AFURLSessionManager (say for testing purposes), you can't just add your NSURLProtocol subclass to session.configuration.protocolClasses, you must instead add it to the beginning of the array (or at least in front of the behavior you're overwriting/modifying).

like image 131
ryanwa Avatar answered Nov 03 '22 01:11

ryanwa


So, firstly @dopcn is right that you need to retain your instance of AFHTTPSessionManager.

@ryanwa was partially correct that you need to insert the new protocol at the start of the array, but it still won't work because the configuration can't be changed after the NSURLSession is made. The correct solution is to use initWithSessionConfiguration:

NSURLSessionConfiguration *sessionConfiguration = [NSURLSessionConfiguration defaultSessionConfiguration];
NSMutableArray * protocolsArray = [sessionConfiguration.protocolClasses mutableCopy];
[protocolsArray insertObject:[MyURLProtocol class] atIndex:0];
sessionConfiguration.protocolClasses = protocolsArray;
AFHTTPSessionManager * myManager = [[AFHTTPSessionManager alloc] initWithSessionConfiguration:sessionConfiguration]; //retain this somewhere!

As far as I'm aware you don't need to register your NSURLProtocol subclass with registerClass. This worked for me on iOS 9.1 on an iPad Mini 4.

From the docs on NSURLSession:

@property(readonly, copy) NSURLSessionConfiguration *configuration

Description

A copy of the configuration object for this session. (read-only) Changing mutable values within the configuration object has no effect on the current session, but you can create a new session with the modified configuration object.

like image 21
Sam Avatar answered Nov 03 '22 01:11

Sam


Hi [AFHTTPSessionManager manager] doesn't return a singleton object instead it returns a brand new instance. So if you just set protocolClasses in didFinishLaunchingWithOptions for one instance of AFHTTPSessionManager but use another instance created by [AFHTTPSessionManager manager] elsewhere the new manager doesn't has your custom protocol class registered. This might lead to problem.

like image 29
dopcn Avatar answered Nov 03 '22 00:11

dopcn