4
\$\begingroup\$

I am pretty new to iOS development - I'm writing an app the uses web services, pretty extensively. With that in mind, I decided to use AFNetworking 2.0 and subclass AFHTTPSessionManager.

I created a method to build a URL for me - based on NSDictionary parameters and then I use the URL with the parameters to execute a GET / POST / PUT request. My worry is the last part - have I made this class less readable and less manageable by doing that?

BBWebService.h

#import <Foundation/Foundation.h>
#import "AFNetworking.h"
@interface BBWebService : AFHTTPSessionManager
@property (strong, nonatomic) NSData *responseData;


    //Designated Initilizer

- (id) initWithURL: (NSString*) url RequestType: (NSString*) requestType PostDataValuesAndKeys: (NSDictionary*) postData RequestProperties: (NSDictionary*) requestProperties UrlParameters: (NSDictionary*) urlParameters;


@end

BBWebService.m

import "BBWebService.h"

@interface BBWebService ()



@end
@implementation BBWebService

- (id) initWithURL: (NSString*) url RequestType: (NSString*) requestType PostDataValuesAndKeys: (NSDictionary*) postData RequestProperties: (NSDictionary*) requestProperties UrlParameters: (NSDictionary*) urlParameters 
{
    self = [super init];
    if (self)
        {
        NSString* urlWithParameters = [NSString stringWithString: url];

            // check to see if url parameters have been specified
        if (urlParameters != nil)
            {
            NSString *paramString = nil;

                // Work-around for URLs that may use a parameter to indicate an "action" instead of a path in the URL itself.
            BOOL hasQuestionMark = NO;
            NSRange textRange;
            textRange = [urlWithParameters rangeOfString:@"?"];

            if(textRange.location != NSNotFound)
                {
                hasQuestionMark = YES;
                }

            for (int i = 0; i < [[urlParameters allKeys] count]; i++)
                {
                if (i == 0)
                    {
                    if (hasQuestionMark == YES)
                        {
                        paramString = [NSString stringWithFormat: @"&%@=%@",
                                       [[urlParameters allKeys] objectAtIndex: i],
                                       [urlParameters objectForKey:[[urlParameters allKeys] objectAtIndex: i]]];
                        }
                    else
                        {
                        paramString = [NSString stringWithFormat: @"?%@=%@",
                                       [[urlParameters allKeys] objectAtIndex: i],
                                       [urlParameters objectForKey:[[urlParameters allKeys] objectAtIndex: i]]];
                        }


                    }
                else
                    {
                    paramString = [NSString stringWithFormat: @"%@&%@=%@", paramString,
                                   [[urlParameters allKeys] objectAtIndex: i],
                                   [urlParameters objectForKey:[[urlParameters allKeys] objectAtIndex: i]]];
                    }
                }

            urlWithParameters = [url stringByAppendingString:paramString];
            urlWithParameters = [urlWithParameters stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];

                NSLog(@"Url with encoded parameters: %@", urlWithParameters);
            }

 __unused   AFHTTPSessionManager* sessiomNamager = [BBWebService buildRequest:urlWithParameters RequestType:requestType PostDataValuesAndKeys:postData RequestProperties:requestProperties];

        }

    return self;
}


    //Class method

+ (AFHTTPSessionManager *) buildRequest: (NSString*) url RequestType: (NSString*) requestType PostDataValuesAndKeys: (NSDictionary*) postData  RequestProperties: (NSDictionary*) requestProperties
{

    AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
    manager.responseSerializer = [AFHTTPResponseSerializer serializer];


    if ([requestType isEqualToString:@"GET"])
        {
        [manager GET:url parameters:postData success:^(NSURLSessionDataTask *dataTask, id responseObject){

                //Success
            NSLog (@"Success");


        }
             failure:^(NSURLSessionDataTask *dataTask, NSError *error){

                     //Failure
                 NSLog (@"Failure");


             }];

        }else if ([requestType isEqualToString:@"POST"]){

            [manager POST:url parameters:postData success:^(NSURLSessionDataTask *dataTask, id responseObject){

                    //Success
                NSLog (@"Success!!");

            }
                  failure:^(NSURLSessionDataTask *dataTask, NSError *error){

                          //Failure
                      NSLog (@"Failure");


                  }];

        }else if ([requestType isEqualToString:@"PUT"]){

            [manager PUT:url parameters:postData success:^(NSURLSessionDataTask *dataTask, id responseObject){

                    //Success
            }
                 failure:^(NSURLSessionDataTask *dataDask, id responseObject){

                         //Failure
                 }];
        }


    return manager;
}


@end

Questions:

  1. In my designated initWithURL method - is this a good way to build a URL based on NSDictionary parameters?

  2. With the class method: builRequest - I looked in the header file of AFHTTPSessionManager.h - I couldn't see a way to properly detect the requestType (GET/POST, etc) - and this is why I am checking with an NSString value.

I worry that should someone else use this class and they type / spell the GET / POST part wrong - the service won't run and it may be a hard to track bug as the compiler won't show a warning for NSString values.

This is how I would use this method in another class:

BBWebService *newWebService = [[BBWebService alloc]initWithURL:URL RequestType:@"GET" PostDataValuesAndKeys:nil RequestProperties:nil UrlParameters:nil];

I am aware I have not added the class method to the header file - reason being is I am still finishing the rest of the file - and I have not decided yet if I want it exposed.

\$\endgroup\$
  • \$\begingroup\$ @Tender I am also looking for same thing but could not able to handle if Service is Get, post..... Please if u could update ur complete answer then it would be great for us to handle \$\endgroup\$ – ChenSmile Jun 8 '15 at 6:31
1
\$\begingroup\$

My first thought is does AFNetworking not handle this already but if not I would suggest doing this in a more AFNetworking style: create an object of type AFHTTPResponseSerializer * and set an instance of it as the session manager's requestSerializer property

This way you can use the default session managers request methods (i.e. using the get methods and not having to worry about spelling GET, POST etc correctly), and you can be responsible for how the request is formatted by implementing

- (NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request
                               withParameters:(NSDictionary *)parameters
                                        error:(NSError *__autoreleasing *)error;

Adding comment into answer: basically i think the request creation should happen in the requestSerializer object, not in the session manager itself. its ok to do this yourself (this is why AFNetworking provides the property to modify, and protocol to implement), but the session manager (subclass or not) isn't the place for it

\$\endgroup\$
  • \$\begingroup\$ I have looked through the various documents on AFNetworking 2.0 - and cannot see anything related to this. Would I need a singleton of the BBWebservice then to use that correctly? Also - wouldn't I still need to implement the success / fail blocks - I really don't want to do that outside of BBWebService - I'll be calling GET / POST requests multiple times in the app and this is why I tried to make a class just for those methods. Sorry if this is really silly - still trying to figure it out. Appreciate the feedback. \$\endgroup\$ – Robert J. Clegg Oct 30 '13 at 11:40
  • \$\begingroup\$ you could use a subclass of session manager which always creates an appropriate serializer. to me the BBWebService class seems to much like fighting the framework you are using. Also the build request method is kind of hard to understand since it doesn't actually build the request: it runs it \$\endgroup\$ – wattson12 Oct 30 '13 at 12:10
  • \$\begingroup\$ also for completion / error blocks you would just use the standard AFNetworking blocks. I can't see where you are passing blocks to the build request method anyway? \$\endgroup\$ – wattson12 Oct 30 '13 at 12:11
  • \$\begingroup\$ BBWebService is a subclass of AFHTTPSessionManager. In the completion blocks - I plan on running a separate method [self requestDone] that takes the responseObject as an argument. Maybe I am subclassing AFHTTPSessionManager incorrectly. What I need to really do in this class is: take the base url and it's parameters and build the url - which I do with the initWithUrl method. Then I need to check if it was GET/POST/PUT request and then run it. Once GET/POST has ran - I take the responseObject and store it in NSData and use that to parse the XML/JSON data and return it using a protocol. \$\endgroup\$ – Robert J. Clegg Oct 30 '13 at 12:41
  • \$\begingroup\$ So it would go like this: Call the web service with a URL and parameters - BBWebService class builds URL with parameters. Then I check which type of request (GET/POST/PUT, etc) - then in the completion block call requestDone/Failed methods which take the responseObject as an argument and I store it into NSData and hand it off to the parser. That was the plan.. appreciate the feedback @wattson12 \$\endgroup\$ – Robert J. Clegg Oct 30 '13 at 12:45

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.