5

I find myself repeating a lot of code using AFHTTPRequestOperationManager throughout my code, so I was thinking about subclassing it, so I can set it up once as a singleton, and put all the code in the subclass, as opposed to have it spread out through my project. However on the NSHipster episode on AFNetworking 2.0 (http://nshipster.com/afnetworking-2/), it says:

The main difference in 2.0 is that you'll actually use this class directly, rather than subclass it, for reasons explained in the "Serialization" section.

Since AFNetworking and NSHipster have the same author, I regard this a valid argument.

So my question is, do people subclass AFHTTPRequestOperationManager in order to have most network code in one class, or am I overlooking something in the use of the framework?

Lorenzo B
  • 33,216
  • 24
  • 116
  • 190
koen
  • 5,383
  • 7
  • 50
  • 89
  • 1
    I usually subclass it and also use this subclass as a singleton. Check the quotation linked here: http://stackoverflow.com/a/20773611/653513 But that just might be "an old habbit". One more note: you don't have to subclass it in order to use it as a singleton. – Rok Jarc Dec 28 '13 at 15:47
  • That quote is in regard to `AFHTTPRequestOperation`, NOT `AFHTTPRequestOperationManager`. – andybons Jan 27 '15 at 23:05

2 Answers2

1

This is how I solved it.

I created a new MyDBClient object, of which the AFHTTPRequestOperationManager is a property. MyDBClient is a singleton class. I then call my MyDBClient from my view controllers and let that setup the operation manager and start the request. Advantage of this is also that it is easier to switch between AFHTTPRequestOperationManager (pre iOS7) and AFHTTPPSessionManager (iOS7).

koen
  • 5,383
  • 7
  • 50
  • 89
1

I have an object of a connection class. This broadcasts different notifications to any object can register for via [NSNotificationCenter defaultCenter].

-(void) requestData
{
    [[NSNotificationCenter defaultCenter] postNotificationName:kCuriculumDataSourceFetchingStarted object:nil];

    [_sessionManager setDataTaskDidReceiveDataBlock:^(NSURLSession *session,
                                                      NSURLSessionDataTask *dataTask,
                                                      NSData *data)
     {
        if (dataTask.countOfBytesExpectedToReceive == NSURLSessionTransferSizeUnknown)
            return;

        NSUInteger code = [(NSHTTPURLResponse *)dataTask.response statusCode];
        if (!(code> 199 && code < 400))
            return;

        long long  bytesReceived = [dataTask countOfBytesReceived];
        long long  bytesTotal = [dataTask countOfBytesExpectedToReceive];

        NSDictionary *progress = @{@"bytesReceived": @(bytesReceived),
                                   @"bytesTotal":    @(bytesTotal)};

        [[NSNotificationCenter defaultCenter] postNotificationName:kCuriculumDataSourceProgress object:nil userInfo:progress];
    }];



    [self.sessionManager GET:@"recipient/"
                  parameters:nil
                     success:^(NSURLSessionDataTask *task, id responseObject)
    {
        [[NSNotificationCenter defaultCenter] postNotificationName:kCuriculumDataSourceFetchingSucceeded
                                                            object:nil
                                                          userInfo:@{@"response": responseObject}];
    }
                     failure:^(NSURLSessionDataTask *task, NSError *error)
    {

        NSUInteger code = [(NSHTTPURLResponse *)task.response statusCode];
        NSString *msg;
        switch (code) {
            case kCuriculumDataSourceFetchErrorAPIKeyNotFound:  msg = @"Api Key not found or revoked"; break;
            case kCuriculumDataSourceFetchErrorServerDown:      msg = @"Server Down"; break;
            default:    msg = [error localizedDescription]; break;
        }


        [[NSNotificationCenter defaultCenter] postNotificationName:kCuriculumDataSourceFetchingFailed
                                                            object:nil
                                                          userInfo:@{@"error": msg}];
    }];
}

An object that would write the received data to Core Data would register for kCuriculumDataSourceFetchingSucceeded and can access the received response via notification.userInfo[@"response"].
The ViewController would register for kCuriculumDataSourceFetchingSucceeded, kCuriculumDataSourceFetchingFailed and kCuriculumDataSourceProgress.

I only instantiate one object, I don't have to bother, if it is an singleton or not. And therefor I don't have to subclass it or do some associated object tricks to get an method that would return the singleton object. Classes interested in the data fetched from network will just listen for the notification — they don't have to know the object that fetched the data and they don't have to know if it is the only of its kind.

The connection class object itself can register to a notification that other classes would post to trigger a new data fetch.


A view controller could register for notifications like

-(void)configure
{
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(fetchingStarted:) name:kCuriculumDataSourceFetchingStarted object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(fetchingSucceeded:) name:kCuriculumDataSourceFetchingSucceeded object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(fetchingFailed:) name:kCuriculumDataSourceFetchingFailed object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(dataSourceProgress:) name:kCuriculumDataSourceProgress object:nil];
}

In this case the view controller and the network controller import the same configuration header file that defines the tokens like kCuriculumDataSourceFetchingSucceeded. But as these are plain NSStrings, even this dependency can be avoided easily.

An example for a view controllers method that will handle a notification

-(void)dataSourceProgress:(NSNotification *)notification
{
    float bytesReceived = (float)[notification.userInfo[@"bytesReceived"] longLongValue];
    float bytesTotal = (float)[notification.userInfo[@"bytesTotal"] longLongValue];

    float progress = bytesReceived / bytesTotal;

    dispatch_async(dispatch_get_main_queue(), ^{
        self.progressView.progress = progress;
        self.imgView.layer.mask = self.progressView.layer;

    });
}
vikingosegundo
  • 52,040
  • 14
  • 137
  • 178