2

I have a question on which is best way or the correct way to send AFNetworking results to controller. Is it via delegate or notification?

I created a class to handle make API calls that has the code below. So if imported this class to another controller and call this method to make API call. Should I do delegate or notification?

I have read www.raywenderlich.com/59255/afnetworking-2-0-tutorial and it is using delegates. I also been watched CodeSchool tutorial, which they used notification from Model to Controller.

I added the code below in a hope to better show my question.

AFHTTPSessionManager *manager = [[AFHTTPSessionManager alloc] initWithBaseURL:baseURL];

// notification way inside the BLOCK
[ manager GET:path parameters:params
     success:^(NSURLSessionDataTask *operation, id responseObject) {
         [ [NSNotificationCenter defaultCenter] postNotificationName:notificationName
                                                              object:nil
                                                            userInfo:responseObject ];

     } failure:^(NSURLSessionDataTask *operation, NSError *error) {
         [ [NSNotificationCenter defaultCenter] postNotificationName:notificationName
                                                              object:nil ];
     }];

// delegate way inside the BLOCK
[ manager GET:path parameters:params
     success:^(NSURLSessionDataTask *operation, id responseObject) {

       if ([delegate respondsToSelector:@selector(getUserFeedsDidFinish:resultDict:)])
       {
            [delegate performSelector:@selector(getUserFeedsDidFinish:resultDict:) withObject:self withObject:resultDict];
       }

     } failure:^(NSURLSessionDataTask *operation, NSError *error) {
       if ([delegate respondsToSelector:@selector(getUserFeeds:didFailWithResultDict:)]) {
           [delegate performSelector:@selector(getUserFeeds:didFailWithResultDict:)
                          withObject:self
                          withObject:[NSDictionary dictionaryWithObject:error.userInfo forKey:KEY_ERRORS]];
        }
     }];
dr.calix
  • 667
  • 8
  • 21

4 Answers4

3

I will recommend use blocks, how? I will write a service for you, this one is wrote in a class called Connection:

+(void)requestLocation:(NSString*)googleReference completionBlock:(void (^)(NSString * coordinates, NSError * error)) handler{

NSString * urlString = @"https://maps.googleapis.com/maps/";
NSMutableDictionary * parametersDictionary = [NSMutableDictionary dictionary];
[parametersDictionary setObject:googleReference forKey:@"reference"];
[parametersDictionary setObject:@"true" forKey:@"sensor"];
[parametersDictionary setObject:@"key(it is not)" forKey:@"key"];

AFHTTPClient *HTTPClient = [AFHTTPClient clientWithBaseURL:[NSURL URLWithString:urlString]];
NSURLRequest *URLRequest = [HTTPClient requestWithMethod:@"GET" path:@"api/place/details/json" parameters:parametersDictionary];
AFHTTPRequestOperation *requestOperation = [[AFHTTPRequestOperation alloc] initWithRequest:URLRequest];
[requestOperation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
    NSError * error = nil;
    NSDictionary * response = [NSJSONSerialization JSONObjectWithData:responseObject options:NSJSONReadingMutableContainers error:&error];
    NSDictionary * dicGeo = [((NSDictionary*)[response objectForKey:@"result"]) objectForKey:@"geometry"];
    NSDictionary * coords = [dicGeo objectForKey:@"location"];
    NSNumber * lat = [coords objectForKey:@"lat"];
    NSNumber * lng = [coords objectForKey:@"lng"];
    NSString * coordinates = [NSString stringWithFormat:@"%@,%@", lat.description, lng.description];
    handler(coordinates, error);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
    NSLog(@"%@", error);
}];

[requestOperation start];
}

Then to call this service:

   [Connection requestLocation:@"google reference (it is not)" completionBlock:^(NSString *coordinates, NSError *error) { 
    //Your code with results.
 }
peig
  • 418
  • 3
  • 11
  • I think AFJSONRequestOperation is not under AFNetworking right now: http://stackoverflow.com/questions/21294178/replacement-for-afjsonrequestoperation-in-afnetworking-2-x – peig Mar 17 '15 at 16:42
  • You should use a response serialzier: `operation.responseSerializer = [AFJSONResponseSerializer serializer];` but anyway: your code must be using AFNetworking 1.x, as AFHTTPClient is not in available in AFNetworking 2. – vikingosegundo Mar 17 '15 at 16:53
  • the answer in your link also recommend that. – vikingosegundo Mar 17 '15 at 16:55
0

I've only scratched the surface with AFNetworking. From what I've seen, most of it seems to use a third approach, blocks.

Blocks are somewhat new, and different than both delegates and notifications.

Blocks are an extension to C function pointers that let you pass code into a method when you call it.

A common design pattern using blocks is to create a method that takes a completion block. A completion block is a piece of code that gets invoked when an async request is completed.

Take the AFNewtworking method HTTPRequestOperationWithRequest as an example. That method takes a success block, that gets called if the request succeeds, and a failure block, that gets called if the request fails.

Duncan C
  • 128,072
  • 22
  • 173
  • 272
0

Block is the easiest way to use IMO. You don't need to implement extra delegate methods or you don't need any conformations.

Basically define your wrapper like this.

typedef void(^SampleRequestCompletion)(NSError *error, id data);

- (void)GET:(NSString *)URLString
 parameters:(NSDictionary *)parameters
 completion:(SampleRequestCompletion)completion
{
[self GET:URLString parameters:parameters success:^(AFHTTPRequestOperation *operation, id responseObject) {

    // Do what you want

    if (completion) {
        completion(nil, data);
    }

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

// Failure case

    if (completion) {
        completion(error,nil);
    }
}];
}

And call this method from any objects like this,

    [self GET:path parameters:dictionary completion:^(NSError *error, id data) {

}];

So you can manage what to do whenever the call ends with success or failure.

erenkabakci
  • 442
  • 4
  • 15
-1

As the tutorial recommended, we can extract the web service related code into a module which acts more like a model level thing. Considering the communication between the network module and views, view invoke/start the request on a singleton web service client, once response back the usual workflow would be send the result to view controller and show the data in the views. We don't need to return anything back to network module.

So this workflow is more like a notification than delegation. And set the V as the M's delegate, it's weird.

Notification : Hey, man, I have done my job, it's your turn. Delegation: Hey, man, I have done lots, now I need you cover/back up/provide me some tasks, then I will continue/complete the work.

In some situations, it's difficult to choose which one better. For AFNetworking, I thought the Notification approach better.

Leo Jiang
  • 1
  • 1
  • Notifications can't say "hey you" as the have no idea, who is listening. It is a broadcasting mechanism. Delegation also does cover networking, as the networking delegator might say:"I am done fetching. Delegate, do what ever you want". Apples own networking classes work like this. Also networqking does not belong in a model, but controller. But not a view controller. – vikingosegundo Apr 02 '15 at 22:23
  • Yeah, might you're right about whether the networking module belong to a controller. But as the tutorial show, we could subclass the AFNetworking class and abstract the web service as a module. Since most of web services are acting more like a datasource, IMHO at least we can treat the service module as model, we just get/put (read/write) the data through the interface defined regardless the module is networking or persistent. In Unix world, we could use same process to read the data from network or file. – Leo Jiang Apr 03 '15 at 10:26
  • And for the Notification or Delegation, I'm still investigating but currently I chose the Notification. We can't narrow our mind just because Apple use delegate. In my practice, notification would get more clean code, and decouple the caller and callee especially on the params if you would change the design. It's more convenient to use dictionary than explicit params. I just got these from my experiences that I found I need to change the delegate method signature sometimes. If you have better practice, would you share more details? Thank you in advance. – Leo Jiang Apr 03 '15 at 10:44
  • as all other posters says: blocks. – vikingosegundo Apr 03 '15 at 11:19
  • Thank you! After do some experiments, I found block might be another choice. Sounds I need to design the code structure better to decouple the error handling. – Leo Jiang Apr 03 '15 at 15:39