2

Hi,guys, I want to use new APNs apis of iOS8 to handle notification actions. In this method:

- (void)application:(UIApplication *)application
handleActionWithIdentifier:(NSString *)identifier
     forRemoteNotification:(NSDictionary *)notification
         completionHandler:(void (^)())completionHandler {

      if ([identifier isEqualToString:@"ACCEPT_IDENTIFIER"]) {
          [self handleAcceptActionWithNotification:notification];
      }
      else if([identifier isEqualToString:@"MAYBE_IDENTIFIER"]) {
          [self handleMaybeActionWithNotification:notification];
      }
      else if ([identifier isEqualToString:@"REJECT_IDENTIFIER"]) {
          [self handleRejectActionWithNotification:notification];
      }
      else if....blah blah blah..
}

In this case, I may have to write too many if-else statements with NSString in the future, and I knew some way to avoid too many if-else statements,such as using switch, but it's not for string or NSString case.

Is it any solution to avoid writing too many if-else statement in this string or NSString case? Thanks in advance.

rmaddy
  • 298,130
  • 40
  • 468
  • 517
ylovesy
  • 864
  • 1
  • 8
  • 19
  • 2
    I think the way you're doing it is about the cleanest way to do it – Scott Berrevoets Sep 04 '14 at 02:39
  • 1
    I agree with Scott. You might try to create an array or dictionary containing list of possible strings and list of possible actions (selectors to be called) and then loop through that array, but that might be unnecessary complication. – sha Sep 04 '14 at 02:48
  • 1
    In other languages (e.g., C#) you can use a `switch` statement with strings as cases (not just `int`s), but a switch statement is just a series of `if`/`else`s under the hood... – Nicolas Miari Sep 04 '14 at 02:58
  • You can give a try to this http://insobject.blogspot.in/ – itechnician Sep 04 '15 at 11:36

3 Answers3

5

You can put all selectors in a dictionary mapping

NSDictionary* handleMap = @{ 
    @"ACCEPT_IDENTIFIER" : NSStringFromSelector(@selector(handleAcceptActionWithNotification:))
    @"MAYBE_IDENTIFIER" : NSStringFromSelector(@selector(handleMaybeActionWithNotification:))
    @"REJECT_IDENTIFIER" : NSStringFromSelector(@selector(handleRejectActionWithNotification:)])
};

NSString* selString = handleMap[identifier];
if (selString) {
    SEL sel = NSSelectorFromString(selString);
    [self performSelector:sel withObject:notification];
}

handleMap should be declared as member variable so it will be initialised only once.

tia
  • 8,627
  • 1
  • 24
  • 41
2

As long as you name your handler methods correctly you can use an approach like this -

- (void)application:(UIApplication *)application 
handleActionWithIdentifier:(NSString *)identifier 
forRemoteNotification:(NSDictionary *)notification 
completionHandler:(void (^)())completionHandler {

    NSString *selectorStr=[NSString stringWithFormat:@"handle%@:", identifier];


    SEL selector=NSSelectorFromString(selectorStr);

    if ([self respondsToSelector:selector]) {
       [self performSelector:selector withObject:notification];
    }
}

You would then create a series of methods such as

-(void) handleACCEPT_IDENTIFIER:(NSDictionary *)notification {
  ....
}

-(void) handleMAYBE_IDENTIFIER:(NSDictionary *)notification {
  ....
}

and so on.

Note, that this technique will generate a compiler warning. For the reason why see the answer to this question - In the case where your methods return void you can ignore the warning.

Community
  • 1
  • 1
Paulw11
  • 95,291
  • 12
  • 135
  • 153
  • 1
    Note that this can be a huge security risk if identifier is not purely internal. If it's user generated, or generated by an external application, it can be used to execute arbitrary functions in your application. – David Berry Sep 04 '14 at 04:36
  • as long as those arbitrary methods start with the word "handle" – Paulw11 Sep 04 '14 at 04:37
  • Yeah, that's one way to reduce the risk, but a more explicit solution, such as that by @tia, is going to be safer. – David Berry Sep 04 '14 at 04:39
  • Tell me how you can manipulate the identifier to execute an arbitrary method – Paulw11 Sep 04 '14 at 04:40
  • As you pointed out, it can be used to execute any method that starts with handle. A lazy implementation that just used the identifier directly would allow the execution of any selector. Just saying that allowing method names to be directly specified by outside agents is a security issue in general. In this case, the identifier theoretically only comes from an authenticated sever, so should be safe. Using the same approach for a web server or command line processor could be very dangerous. Just need to know what your usage is and where the strings come from. – David Berry Sep 04 '14 at 04:44
  • Fair enough to point out the risk, but in this case I would think that the inclusion of the prefix mitigates the risk to a reasonable extent. It can't execute "arbitrary" code - merely a method in the current class that starts with "handle" – Paulw11 Sep 04 '14 at 04:45
0

It may seems numerous but I think it is a kind of good code structure.
Easy to read, easy to maintain.
Because it is explicit, keyword search and code browsing is always trivial. Also caller graph generation and static analysis will be done more throughly (with limits).

Some coders dislike this pattern say "just too many".
I don't see what's the ground of this "Too Many" point.
Too many for who? Typists? Keyboard hardware?

I will always prefer straightforward, explicit code.

9dan
  • 4,038
  • 2
  • 26
  • 42