0

I'm posting a notification in the request failure block:

[manager    POST:path
    parameters:nil
    success:^(AFHTTPRequestOperation *operation, id responseObject) {
        if (operation.response.statusCode == 200) {
            //message delegate
        }
  } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
      [[NSNotificationCenter defaultCenter] postNotificationName:HOST_UNREACHABLE object:operation];
}];

In the method that receives the notification, the completionBlock attribute is nil.

How do I access it without subclassing & overriding?

quantumpotato
  • 9,637
  • 14
  • 70
  • 146
  • Can you clarify? You have a request that takes a completion and a failure block. In the failure block you post a notification. Some other object observes the notification and runs a method, that method wants to see the request completion block? If I have that right, show how you initialize the completion block, show the method in the other class that's fired upon notification. – danh Jun 29 '15 at 00:03
  • @danh done, using the AFNetworking manager – quantumpotato Jun 29 '15 at 00:19
  • Sorry, still don't understand the last part. The observer of nsnotification doesn't ever get a block param, it gets just the notification that was posted. Maybe describe what you want to have happen in that method and post what you've coded so far. – danh Jun 29 '15 at 00:23
  • It gets the notification with the operation as the object. When I read that object, from notification.object.completionBlock, it's nil. – quantumpotato Jun 29 '15 at 01:19
  • Sorry - I missed that you were passing the operation as the notification object. I can't say for sure, but it's plausible that the AFHTTP operation nils out the completion block when invoking the error and vice versa. It would also be good practice for it to nil out the block it ran, after it invoked it. You could put your own copy of one or both blocks on the stack (in a variable scoped to the method that runs the request) and pass those to the notification. I can show you how to do that, but it seems awfully strange. Can you explain what you hope to accomplish? – danh Jun 29 '15 at 01:28
  • I'm hoping to simply retry a request that failed pointing to a different URL. – quantumpotato Jun 29 '15 at 02:53

1 Answers1

0

First to answer the question of how to send a block to an NSNotification:

The way you're attempting to do it is dangerous, because we don't know how AFHTTPSessionManager handles the blocks you pass it, and, unless its in the public interface, what it does may not remain fixed over time.

So, make a local variable to represent the block you want to pass, say, the completionBlock...

// this is a local variable declaration of the block
void (^completionBlock)(AFHTTPRequestOperation*,id) = ^(AFHTTPRequestOperation *operation, id response) {
    if (operation.response.statusCode == 200) {
        //message delegate
    }
};

[manager POST:path
    parameters:nil
    success:completionBlock
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
    [[NSNotificationCenter defaultCenter] postNotificationName:HOST_UNREACHABLE object:completionBlock];
}];

The observer can get the block and invoke it this way...

- (void)didReceiveNotification:(NSNotification *)notification {
    void (^completionBlock)(AFHTTPRequestOperation*,id) = notification.object;

    // use it
    [manager POST:someOtherPath
        parameters:nil
        success:completionBlock
        // etc.
    ];
}

But I think the approach is strange. It spreads out responsibility for making the request to the object that gets notified, leaving it needing to know the path to retry the parameters (which you don't have in this case, but you might one day).

Consider instead subclassing the manager and adding behavior that does the retry. Now your manager subclass can be in charge of all requests, including retries, and your other classes are just customers who handle the outcome. Something like...

@interface MyAFHTTPRequestManager : AFHTTPSessionManager

- (nullable NSURLSessionDataTask *)POST:(NSString *)URLString
                      retryURL:(NSString *)retryURLString
                    parameters:(nullable id)parameters
                       success:(nullable void (^)(NSURLSessionDataTask *task, id responseObject))success
                       failure:(nullable void (^)(NSURLSessionDataTask *task, NSError *error))failure;

@end

Have your subclass implementation call super with the first URLString, and upon failure, call super with the retryURLString. e.g.

- (nullable NSURLSessionDataTask *)POST:(NSString *)URLString
                      retryURL:(NSString *)retryURLString
                    parameters:(nullable id)parameters
                       success:(nullable void (^)(NSURLSessionDataTask *task, id responseObject))success
                       failure:(nullable void (^)(NSURLSessionDataTask *task, NSError *error))failure {

    [super POST:URLString parameters:parameters success:success
        failure:^(AFHTTPRequestOperation *operation, NSError *error) {
            [super POST:retryURLString parameters:parameters success:success failure:failure];
    }];
}
danh
  • 62,181
  • 10
  • 95
  • 136