netwire
netwire

Reputation: 7215

AFNetworking 2.0 AFHTTPSessionManager performance in a delegate

I currently use a delegate, calling it APIDelegate to handle API-related requests from my View Controllers. The excerpt from the delegate looks like this (Swift):

init(delegate: APIDelegateProtocol?) {
    self.delegate = delegate
}

func get(url: String, params: NSDictionary?) {
    ...blah blah...
    let manager = AFHTTPSessionManager(baseURL: baseURL, sessionConfiguration: sessionConfig)        
    manager.GET(
        url,
        parameters: params,
        success: { (task: NSURLSessionDataTask!, responseObject: AnyObject!) in
            let httpResponse: NSHTTPURLResponse = task.response as NSHTTPURLResponse
            if httpResponse.statusCode == 200 {
                self.delegate?.didReceiveAPIResults(responseObject as NSDictionary)
            } else {
                NSLog("GET Error -> Code: \(httpResponse.statusCode), Msg: \(responseObject)")
            }
        },
        failure: { (task: NSURLSessionDataTask!, error: NSError!) in
            dispatch_async(dispatch_get_main_queue(), {
                UIAlertView(title: "Networking Error", message: "GET Error -> \(url) -> \(error.localizedDescription)", delegate: nil, cancelButtonTitle: "OK").show()
                }
            )
        }
    )
}

However, according to NSScreenCast episode on AFNetworking 2.0 and a few other web tutorials, they seem to recommend to create Singleton that is subclassed from AFHTTPSessionManager, something like this (copy & pasted in Obj-C):

+ (ITunesClient *)sharedClient {
    static ITunesClient *_sharedClient = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        NSURL *baseURL = [NSURL URLWithString:@"https://itunes.apple.com/"];

        NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
        [config setHTTPAdditionalHeaders:@{ @"User-Agent" : @"TuneStore iOS 1.0"}];

        NSURLCache *cache = [[NSURLCache alloc] initWithMemoryCapacity:10 * 1024 * 1024
                                                          diskCapacity:50 * 1024 * 1024
                                                              diskPath:nil];

        [config setURLCache:cache];

        _sharedClient = [[ITunesClient alloc] initWithBaseURL:baseURL
                                         sessionConfiguration:config];
        _sharedClient.responseSerializer = [AFJSONResponseSerializer serializer];
    });

    return _sharedClient;
}

My question is: What is the advantage of using a Singleton subclass of AFHTTPSessionManager versus just calling AFHTTPSessionManager directly within the APIDelegate? Is it better for memory management? Isn't AFHTTPSessionManager already non-blocking with respect to the View Controller (the call is asynchronous)?

Upvotes: 0

Views: 1982

Answers (1)

Aaron Brager
Aaron Brager

Reputation: 66242

Isn't AFHTTPSessionManager already non-blocking with respect to the View Controller (the call is asynchronous)?

Yes, both approaches are non-blocking.

What is the advantage of using a Singleton subclass of AFHTTPSessionManager versus just calling AFHTTPSessionManager directly within the APIDelegate? Is it better for memory management?

There are no significant memory differences. (The only relevant difference is that you could deallocate an APIDelegate instance, but a singleton will hang around for the lifetime of the app.)

One advantage of a subclass over the approach you're taking is that you might have a view controller with more than one possible API call. However, in this case both responses would result in a call to didReceiveAPIResults, which would then need to try to figure out which API call was just responded to. This can be error prone, since network requests don't always return in the order they're made.

If you take the singleton approach, your manager can still hide all of the networking from your view controller.

For example, if you're writing an app that lists cheeses for sale, your header file could include:

- (void)getCheeseListWithSuccess:(void (^)(NSArray *cheeses))success
                         failure:(void (^)(NSError *error))failure;

This hides the data source from your view controller, which shouldn't care if it came from an on-disk cache, the Internet, or a random cheese generator.

To answer your question more succinctly, neither solution is formally incorrect, but a solution that uses completion handlers instead of delegation is probably better.

Upvotes: 4

Related Questions