50

I'm in the process of rethinking my approach to the request architecture of a large app I'm developing. I'm currently using ASIHTTPRequest to actually make requests, but since I need many different types of requests as a result of many different actions taken in different view controllers, I'm trying to work out the best system of organizing these requests.

I'm currently building singleton "requesters" that are retained by the app delegate and sit around listening for NSNotifications that signal a request needs to be made; they make the request, listen for the response, and send out a new NSNotification with the response data. This solves most of my problems, but doesn't elegantly handle failed requests or simultaneous requests to the same singleton requester.

Anyone have any success devising a clear, OO architecture for making many different types of requests in an iOS app?

kevboh
  • 5,207
  • 5
  • 38
  • 54
  • 2
    Good question: I've been researching for an answer to this as well, as someone who has created a connection manager for NSURLConnection that works well for one connection but not so well for several. – Alan Zeino Jan 26 '11 at 23:10

4 Answers4

68

After having tried several approaches, this is one architecture that is giving me excellent results, is easy to document, understand, maintain and extend:

  • I have a single object taking care of network connectivity, let's call it a "network manager". Typically this object is a singleton (created using Matt Gallagher's Cocoa singleton macro).
  • Since you use ASIHTTPRequest (which I always do, wonderful API) I add an ASINetworkQueue ivar inside my network manager. I make the network manager the delegate of that queue.
  • I create subclasses of ASIHTTPRequest for each kind of network request that my app requires (typically, for each backend REST interaction or SOAP endpoint). This has another benefit (see below for details :)
  • Every time one of my controllers requires some data (refresh, viewDidAppear, etc), the network manager creates an instance of the required ASIHTTPRequest subclass, and then adds it to the queue.
  • The ASINetworkQueue takes care of bandwidth issues (depending on whether you are on 3G, EDGE or GPRS or Wifi, you have more bandwidth, and you can process more requests, etc). This is done by the queue, which is cool (at least, that's one of the things I understand this queue does, I hope I'm not mistaken :).
  • Whenever a request finishes or fails, the network manager is called (remember, the network manager is the queue's delegate).
  • The network manager doesn't know squat about what to do with the result of each request; hence, it just calls a method on the request! Remember, requests are subclasses of ASIHTTPRequest, so you can just put the code that manages the result of the request (typically, deserialization of JSON or XML into real objects, triggering other network connections, updating Core Data stores, etc). Putting the code into each separate request subclass, using a polymorphic method with a common name accross request classes, makes it very easy to debug and manage IMHO.
  • Finally, I notify the controllers above about interesting events using notifications; using a delegate protocol is not a good idea, because in your app you typically have many controllers talking to your network manager, and then notifications are more flexible (you can have several controllers responding to the same notification, etc).

Anyway, this is how I've been doing it for a while, and frankly it works pretty well. I can extend the system horizontally, adding more ASIHTTPRequest subclasses as I need them, and the core of the network manager stays intact.

Hope it helps!

Adrian Kosmaczewski
  • 7,956
  • 4
  • 33
  • 59
  • good answer! how do you test your system? another big concern of mine is coming up with an architecture that's easy to unit test. – kevboh Jan 28 '11 at 00:04
  • 5
    One quick thing that springs to mind, you could possibly use blocks instead of notifications if you only have one class that is interested in your response. – Michael Gaylord Apr 05 '11 at 13:32
  • @Michael yes, but only if your app runs exclusively in iOS 4.x. As I have several projects backwards-compatible with 3.2, for the moment I stick with plain'ol notifications. – Adrian Kosmaczewski Apr 05 '11 at 17:15
  • 1
    As an update, I'm now using the architecture fairly heavily (we have 20-30 different types of requests) and it scales admirably. – kevboh Jun 01 '11 at 15:17
  • @kevboh glad to hear that! Any feedback, comment or critics would be greatly appreciated. – Adrian Kosmaczewski Jun 02 '11 at 11:54
  • 7
    I've just published a blog post that shows a sample implementation of this architecture: http://akosma.com/2011/09/20/a-proposed-architecture-for-network-bound-ios-apps/ – Adrian Kosmaczewski Sep 20 '11 at 16:02
  • 1
    Given the recent announcement that ASIHTTPRequest is not going to be supported anymore, I'm going to adapt Senbei to support AFNetworking in the near future. – Adrian Kosmaczewski Oct 15 '11 at 10:08
  • @kevboh: Can you provide a URL? – Bill Craun May 10 '12 at 19:39
  • @akosma, Quick question, but first, thank you for your contribution. After it having been nearly 3 years, I wanted to get an update hoping for you to have finally supported Senbei to AFNetworking. Could you let me know the update on that please? – Pavan Apr 30 '14 at 02:01
  • @akosma Is your implementation same as mine? https://github.com/kevin0571/STNetTaskQueue – Kevin Feb 10 '15 at 03:35
1

Here is how I generally do it. I, too, have a singleton object used for making network requests. For requests that have to be made often, I have an NSOperationQueue that accepts AFHTTPRequestOperations (or AFJSONRequestOperations) since I generally use AFNetworking for making requests. For these, there is a completionBlock and failureBlock property that is executed upon success or failure of the request. On my singleton object, I would have a method for initiating a particular network request, and as parameters to that method, I would include a success and failure block which can be passed into the blocks defined in the method. This way, the entire application can make a network request, and the scope of the application at that point is available to the singleton in the block that is passed to the method. For example...(using ARC)

        @implementation NetworkManager

    -(void)makeRequestWithSuccess:(void(^)(void))successBlock failure:(void(^)(NSError *error))failureBlock

    {
        NSURL *url = [NSURL URLWithString:@"some URL"];

        NSURLRequest *request = [NSURLRequest requestWithURL:url];

        AFHTTPRequestOperation *op = [[AFHTTPRequestOperation alloc] initWithRequest:request];

        [op setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
            [responseObject doSomething];

            if (successBlock)
                dispatch_async(dispatch_get_main_queue(), successBlock);

        } failure:^(AFHTTPRequestOperation *operation, NSError *error) {

            if (failureBlock)
                dispatch_async(dispatch_get_main_queue(), ^{
                    failureBlock(error);
                });
        }];

        [self.operationQueue addOperation:op];
    }
@end

And you can always make the success block take whatever parameters you need to pass around.

Jacob
  • 926
  • 1
  • 14
  • 25
0

Try out STNetTaskQueue, which can make your request reusable and maintainable.

Kevin
  • 1,147
  • 11
  • 21
0

The fully-loaded project is a good read.

ohho
  • 50,879
  • 75
  • 256
  • 383
  • I've seen fully-loaded before- it handles a specific problem well, but I'm looking for a solution that treats with different types of requests that have different responses. The way fully-loaded handles requests is similar to what I've detailed in my question, with nsnotifications and asihttprequest doing the heavy lifting. I'm looking for something more extensible and robust, even if it's just a creative way to organize notifications and requests. – kevboh Jan 27 '11 at 03:30
  • 1
    The other code example in my mind is the `Networking` folder in the MVCNetworking sample from Apple, which I do not have any experience to make any comment though. – ohho Jan 27 '11 at 03:54
  • Interesting. I'll have to check this out. – kevboh Jan 27 '11 at 15:23