Search code examples
objective-crefactoringafnetworking-2reactive-cocoa

How to refactor the almost identical logic when using AFNetworking and ReactiveCocoa?


I have several methods that return signals that created by

+ (RACSignal *)createSignal:

And in the signals I send different requests using the methods from AFHTTPRequestOperationManager in AFNetworking:

- GET:parameters:success:failure
- POST:parameters:success:failure
- PUT:parameters:success:failure
- DELETE:parameters:success:failure

Supposed that I have a property to store the instance of AFHTTPRequestOperationManager:

@property(strong, nonatomic) AFHTTPRequestOperationManager *manager;

This is the body of the GET request method, others are almost identical with it except the method calls from AFHTTPRequestOperationManager:

- (RACSignal *)GET:(NSString *)url parameters:(NSDictionary *)parameters {
  @weakify(self)
  return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
    @strongify(self)
 // [self.manager POST:...
 // [self.manager PUT:...
 // [self.manager DELETE:...
    [self.manager GET:url parameters:parameters success:^(AFHTTPRequestOperation *operation, NSDictionary *response) {
      [subscriber sendNext:response];
      [subscriber sendCompleted];
  } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
      [subscriber sendError:error];
  }];
  return nil;
}];

And I want to extract the [self.manager GET...] logic out so that can I eliminate the duplicate code and only to pass different selectors(or something like it) to send different requests.

How do I achieve it ?


Solution

  • This is untested, but should get you most of the way there:

    - (RACSignal *)GET:(NSString *)url parameters:(NSDictionary *)parameters {
      SEL sel = @selector(GET:parameters:success:failure:);
      return [self signalForRequest:sel url:url parameters:parameters];
    }
    
    - (RACSignal *)POST:(NSString *)url parameters:(NSDictionary *)parameters {
      SEL sel = @selector(POST:parameters:success:failure:);
      return [self signalForRequest:sel url:url parameters:parameters];
    }
    
    - (RACSignal *)PUT:(NSString *)url parameters:(NSDictionary *)parameters {
      SEL sel = @selector(PUT:parameters:success:failure:);
      return [self signalForRequest:sel url:url parameters:parameters];
    }
    
    - (RACSignal *)DELETE:(NSString *)url parameters:(NSDictionary *)parameters {
      SEL sel = @selector(DELETE:parameters:success:failure:);
      return [self signalForRequest:sel url:url parameters:parameters];
    }
    
    - (RACSignal *)signalForRequest:(SEL)requestSEL url:(NSURL *)url parameters:(NSDictionary *)parameters {
      @weakify(self);
    
      return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        @strongify(self);
    
        void (^success)(AFHTTPRequestOperation *, NSDictionary *) = ^(AFHTTPRequestOperation *operation, NSDictionary *response) {
          [subscriber sendNext:response];
          [subscriber sendCompleted];
        };
        void (^failure)(AFHTTPRequestOperation *, NSDictionary *) = ^(AFHTTPRequestOperation *operation, NSError *error) {
          [subscriber sendError:error];
        };
    
        NSMethodSignature *methodSignature = [self.manager methodSignatureForSelector:requestSEL];
        NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSignature];
        [invocation setSelector:requestSEL];
        [invocation setTarget:self.manager];
        [invocation rac_setArgument:url atIndex:2];
        [invocation rac_setArgument:parameters atIndex:3];
        [invocation rac_setArgument:success atIndex:4];
        [invocation rac_setArgument:failure atIndex:5];
    
        [invocation invoke];
      }];
    }