AFNetworking alternative: STHTTPRequest
github.com
github.com
Networking is an inherently complex problem. Attempting to simplify the problem by ignoring those complexities, rather than actually deal with them, misses the point entirely.
This library in particular demonstrates an unfortunate lack of understanding of why Apple has designed its networking stack the way it has. As a result, it needlessly duplicates interfaces and functionality of built-in classes.
Not exposing NSURLRequest means that there's no support for caching policies, or HTTP pipelining, or setting an HTTP body stream, or control over cellular access. Not using NSURLCredential or exposing authentication challenges means that you lose support for digest auth, NTLM, and kerberos, and leaves the user vulnerable to Man-in-the-Middle attacks for lack of certificate pinning and verification. Not exposing other delegate methods means no backgrounding support, no cache control, and no extensibility beyond what the original author envisioned.
AFNetworking is modular and composable. If you don't want to deal with its complexity, you can ignore a lot of it. If you don't need much, just use the built in NSURLConnection or NSURLSession classes (they're actually quite nice). But seriously, don't settle for a wrapper library that dumbs down a problem; you'll regret it soon enough.
If i want to generate url requests, i have to use a serializer class. For me, it seems like overkill to have a stateful serializer. Are there a lot of cases where people change the state of the serializer once created? If not, then wouldn't it be better to just expose all the setup functionality through simple wrapper functions instead of wrapper classes, and let people layer these as they see fit (possibly with server-specific categories)?
For multipart requests, why did a protocol with appends and requiring a block with stateful appends end up making more sense than just taking an array of the parts? My impression is that it feels like a lot of chatter for multipart setup. It seems like something that could just be encapsulated as one output from a set of inputs (function). In practice, i've found this approach works better for me.
No, you don't. There's nothing stopping you from creating `NSURLRequest` directly.
> ...wouldn't it be better to just expose all the setup functionality through simple wrapper functions?
Request serializers are not unlike any other class in Cocoa, like say NSURLSessionConfiguration, which is not often mutated beyond initial setup, but exposes properties to remain flexible and not overwhelm the user with init parameters.
What you're suggesting sounds much, much worse.
> For multipart requests, why did a protocol with appends and requiring a block with stateful appends end up making more sense than just taking an array of the parts?
Not using a protocol / builder pattern here would be awful.
You'd have to create classes for each kind of part, which gets complicated because AFNetworking handles data, files, and streams alike. So that's, like, 3 extra top-level classes. And even that doesn't really work, since it makes it really difficult to just append data to the multipart body manually, in case there was some missing functionality that AFN didn't provide. Add to that the consideration of how to specify options on the stream itself, like throttling...
You should give all of that another look—it's one of the best parts of AFNetworking.
There is a lot of basic http setup missing from NSMutableURLRequest that needs to live somewhere.
> Request serializers are not unlike any other class in Cocoa, like say NSURLSessionConfiguration, which is not often mutated beyond initial setup, but exposes properties to remain flexible and not overwhelm the user with init parameters.
Except NSURLSessionConfiguration is more of a data type, right? By that I mean it is just a blob of parameters that can be inspected. The serializer is not just a configuration, it's something with an 'er' on the end. Plus, many of its properties are direct duplications of NSMutableURLRequest that could just be set after the fact in a wrapper function. AFJSONRequestSerializer and others suggest i follow a pattern of subclass inheritance to provide further customization when everything is just permutations on values of a core set of parameters.
> You'd have to create classes for each kind of part, which gets complicated because AFNetworking handles data, files, and streams alike. So that's, like, 3 extra top-level classes. And even that doesn't really work, since it makes it really difficult to just append data to the multipart body manually, in case there was some missing functionality that AFN didn't provide. Add to that the consideration of how to specify options on the stream itself, like throttling...
I don't understand this argument. What's wrong with creating the simple data types for the cases you need to cover? What is the importance of the top level class count metric? Would a fourth top level data type for the raw data case suffice? Can't throttling go in a parameter of the outer configuration data type?
> You should give all of that another look—it's one of the best parts of AFNetworking.
Maybe. When i look at it i see class hierarchies, singletons, and protocols with contractual state manipulation for a case that seems to (maybe it doesn't?) reduce to an output (NSMutableURLRequest) that's a straight function of a bunch of inputs.
Hiding networking complexity behind a simple interface is exactly the goal of the STHTTPRequest class.
Anyone who needs more flexibility is free to access NSURLRequest directly or to use the library of his choice.
I keep on dreaming of something as simple as the Python's requests module for Cocoa.
FWIW this is exactly what i suspected when i saw it.
We should expose composable pieces to assist with use of the existing APIs, not build monolithic wrappers that get in our way by hiding entire chunks of APIs in the guise of simplicity, since eventually this just requires us to reimplement the needed abstractions in a different way once our wrappers' limitations become apparent.
So today, it's simple. Tomorrow, since it sees more use by people with different needs and it ends up looking a little more like AFNetworking. The next day upstream adds some features that don't quite fit with how simple we thought everything should be, so we twist things a little. And one day we look up and it's all really complicated, so we scrap everything and start again with a nice simple wrapper to hide all that stupid stuff.
And some days later, someone needs to satisfy a network requirement, but since the authors of all of their different dependencies chose different network wrappers (they each had an opinion about what not to expose), everything is really hard to centralize.
I do like the idea of something simpler than AFNetworking though. While AFNetworking is a great library it feels a bit too complex for what it's doing at times. Depending on whether or not I need RestKit in the next project I do I might use this instead of AFNetworking.
Thanks for sharing this.
I think another wonderful addition would be a client side router to match response serializers with request urls.
It makes sense with blocks where you have actual multiple values you can pass in without futzing with pointers to pointers.
> Although Cocoa methods that indirectly return error objects in the Cocoa error domain are guaranteed to return such objects if the method indicates failure by directly returning nil or NO, you should always check that the return value is nil or NO before attempting to do anything with the NSError object.
I wish it were a little bit less cryptic. If it's guaranteed then it's guaranteed, no? Is it possible to get a non-nil error object even after a successful call? Is it possible to have the opposite happen? Makes very little sense.
[Edited]
All that said, using NSURLConnection (or NSURLSession) really isn't annoying anymore.
I'm going to give this an honest try.
Yes. Oh goodness, yes it is. Having consistent validation on the networking level means not having sporadic logic scattered throughout models and view controllers.
Your server sent invalid JSON (e.g. sending HTML instead)? AFNetworking just saved you from a crash, or some other undefined, dangerous behavior.
AFNetworking is pedantic because networking is such a wildcard, in terms of performance, consistency, and security.
> - Success block and error block are called on main thread
Those seem like two quite irritating limitations - anyone know the rationale behind them?
On the main thread, you can always get a running run loop, the main run loop, for free.
When there is async code the expectation is
CODE CODE [start_block { LATER - excuted in main thread }] CODE
Otherwise you write a ton of code with get_main_thread
http://rikacomet.blogspot.in/2014/01/setting-cookies-in-perl...