0
\$\begingroup\$

I'm building an iOS app that uses web services extensively. I've built classes that handle the requests. However since there are a lot of web service request. I need to find out how to detect which web service to run, based on the action - registration, login, image upload, etc.

This is just a small snippet of a very large method I came up with:

- (BOOL) wsExecution:(int) wsID wsParameters: (NSDictionary*) parameters
{

    switch (wsID)
    {
        case WEB_SERVICE_ID
        {


            ClassWS* ws = [[ClassWS alloc] initWithURL:WEB_SERVICE_ID Url:WEB_SERVICE_URL];

            break;
        }
        case AAWEB_SERVICE_ID
        {


            ClassWS* ws = [[ClassWS alloc] initWithURL:AAWEB_SERVICE_ID Url:AAWEB_SERVICE_URL];

            break;
        }

The web service IDs are defined in a file called constants. I then map a URL to an ID in the method above. There are another 30 switch cases in this method. The Web Service IDs are just ints that have no relevance to the actual web service. It was just a way to check which service, based on a switch statement.

It makes it hard to read and I think it can be done better. Is there a way to do with and remove the notion of a web service ID completely?

To be a little more clear on where I am going with this:

Here is another example of the switch case (I have counted 200 cases):

switch (wsID)
    {
        case RUN_ORDER_WS_ID:
        {
            NSDictionary* postData = [NSDictionary dictionaryWithObject:[parameters objectForKey:@"OrderResponse"] forKey:@"OrderResponse"];

            NSMutableDictionary* newParams = [NSMutableDictionary dictionaryWithDictionary:parameters];

            OrderWS* ws = [[OrderWS alloc] initWithURL:RUN_ORDER_WS_ID Url:RUN_ORDER_WS_ID_WS_URL];
            self.placeClassifiedResponseWS = ws;
            ws.delegate = self;
            [newParams removeObjectForKey:@"OrderResponse"];
            [ws fetch:newParams PostDataValuesAndKeys:postData];
            [ws release];

            break;
        }
        case GET_LIST_OF_USERS_ID:
        {
            NSDictionary* postData = [NSDictionary dictionaryWithObject:[parameters objectForKey:@"userList"] forKey:@"userList"];

            NSMutableDictionary* newParams = [NSMutableDictionary dictionaryWithDictionary:parameters];

            UsersWS* ws = [[UsersWS alloc] initWithURL:GET_LIST_OF_USERS_ID Url:GET_LIST_OF_USERS_URL];
            self.userListWS = ws;
            ws.delegate = self;
            [newParams removeObjectForKey:@"userList"];
            [ws fetch:newParams PostDataValuesAndKeys:postData];
            [ws release];

            break;
        }
        case GET_USER_REVIEW_FROM_ITEMS_WS_ID:
        {
            NSString* itemId = [parameters objectForKey:@"itemId"];
            NSMutableDictionary* newParams = [NSMutableDictionary dictionaryWithDictionary:parameters];
            NSString* newURL = [NSString stringWithFormat:GET_USER_REVIEW_FROM_ITEMS_WS_URL, itemId];

            DataStoreWS = [[DataStoreWS alloc] initForComponents: [NSArray arrayWithObjects:REVIEW_COMPONENT, nil] WebserviceID: wsID Url:newURL];
            self.reviewOfUserWS = ws;
            ws.delegate = self;
            [newParams removeObjectForKey:@"itemId"];
            [ws fetch:newParams];
            [ws release];

            break;
        }
\$\endgroup\$
  • 1
    \$\begingroup\$ If you're mapping Url to an invented numeric value 1:1 wouldn't it make sense to rather create a dictionary where the key was the value and the object the URL? Then you could basically replace that switch/case with one simple objectForKey: call? \$\endgroup\$ – T. Benjamin Larsen Nov 4 '13 at 14:57
  • 1
    \$\begingroup\$ What nickfalk said. But also, rethink your naming: initWithURL:(not a URL!) Url:(wait, here's the URL?) \$\endgroup\$ – Flambino Nov 4 '13 at 17:01
  • \$\begingroup\$ Thanks for the feedback gents. Admittedly, I don't know NSDictionary that well - In the morning I will have a look into it and see how it can help me. Off the top of my head I am already asking myself how am I going to do checking without a switch / if statements all over my method. Since there are over 30 web services @Flambino - Completely agree with you there - an oversight on my part. Will correct it. Once I have come up with a solution I will update this question. \$\endgroup\$ – Robert J. Clegg Nov 4 '13 at 19:22
  • \$\begingroup\$ @Tander Once you get something running it's probably wiser to create a new question, if you're still looking for feedback. If you update this question too much, comments and answers here lose their context. Also, absolutely get to know NSDictionary. It's so fundamental to Cocoa that it's got its own JSON-like shorthand syntax (see nickfalk's answer) same as, say, @[...] for NSArray or @"..." for NSString. \$\endgroup\$ – Flambino Nov 4 '13 at 21:35
3
\$\begingroup\$

Here's a quick and dirty example to illustrate the benefit of the dictionary setup:

- (NSDictionary *)webServiceIDsAndURLs{
    NSDictionary *sillyExampleDictionary = @{ @1 : [NSURL URLWithString:@"http://www.noreagle.com"],
                                              @2 : [NSURL URLWithString:@"http://www.stackOverflow.com"],
                                              @3 : [NSURL URLWithString:@"http://www.etc.com"]};
    return sillyExampleDictionary;
}

...

- (BOOL) webServicesExecution:(NSNumber *) webServiceID webServiceParameters: (NSDictionary*) parameters{
    NSURL *URL = [[self webServiceIDsAndURLs] objectForKey:webServiceID];
    ClassWebService *webService = [[ClassWebService alloc] initWithID:webServiceID URL:URL];

    // see, no lengty switch, yay!

    return YES; // Not sure what you are planning here...
}
\$\endgroup\$
  • \$\begingroup\$ Thanks for the quick example - much appreciate. The problem is that I won't be executing the same class / method on all web service IDs. Depending on the web service ID - I execute a few different web services - /search / image upload / buyer info - etc. Would NSDictionary allow for this sort of thing? \$\endgroup\$ – Robert J. Clegg Nov 5 '13 at 6:10
  • \$\begingroup\$ The more I think about this then more I don't see how to get passed a switch statement. I'm telling the code to do this: "If web service is (ID or URL, doesn't matter) then do this" now the last action is anything from running a method in another class to creating and dictionary. \$\endgroup\$ – Robert J. Clegg Nov 5 '13 at 7:16
  • \$\begingroup\$ Without detailed knowledge about your project its hard to be very concrete. It sounds like you will need SOME logic outside the dictionary stuff. It do sound however that you would be able to trim down the logic of the switch statement quite a lot if you limit this to actually messaging the methods: Find the URL from the dictionary first then lump together whatever ID's that makes sense to perform the different services... \$\endgroup\$ – T. Benjamin Larsen Nov 5 '13 at 7:39
  • \$\begingroup\$ I have updated my question with a more complete version of the switch statement. I really like the NSDictionary idea - but it seems I can't get passed the comparison that the switch does.. 200 odd times.. any thoughts? Appreciate the feedback. \$\endgroup\$ – Robert J. Clegg Nov 5 '13 at 7:55
  • \$\begingroup\$ Only knowing what you show me I'd say 1.Put the creation of the postData dictionary outside the switch statement, it is calling the same method after all. Pass the key into the method instead of defining it inside the method scope. Those two things alone will leave you with only the two different User/order instances and the properties. \$\endgroup\$ – T. Benjamin Larsen Nov 5 '13 at 8:13

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.