3

Is there any way to test selectors/methods when all you have is a proxy object?

/* Proxy which may forward
 * the method to some other object */ 
id proxy = [UINavigationBar appearance];

/* This condition returns FALSE
 * despite the fact that the method would have otherwise been
 * successfully executed at its destination */
if([proxy respondsToSelector:@selector(setBarTintColor:)]){
    [proxy setBarTintColor:color];
}
rmaddy
  • 314,917
  • 42
  • 532
  • 579
eric
  • 4,863
  • 11
  • 41
  • 55
  • @maddy that's why I'm trying to find a way to test whether the method call will be successful before calling it -- rather than having the logic based on the OS version.. – eric Sep 27 '13 at 18:43

2 Answers2

2

Apparently you cannot.

The methods suggested by other answers will break easily, here's an example:

  • UINavigationBar instances respond to selector setTranslucent:
  • However setTranslucent: is not tagged with UI_APPEARANCE_SELECTOR in the header file

Therefore the following code

if([[UINavigationBar class] instancesRespondToSelector:@selector(setTranslucent:)]) {
    [[UINavigationBar appearance] setTranslucent:NO]; // BUM!
}

will result in a crash.

The only information about which selectors conform to UIAppearance seems to be the UI_APPEARANCE_SELECTOR macro, which is stripped at compile-time.

A runtime check doesn't look to be feasible.


For the sake of completeness, here's an awful (but practical) way of doing it.

@try {
    [[UINavigationBar appearance] setTranslucent:YES];
} @catch (NSException *exception) {
    if ([exception.name isEqualToString:@"NSInvalidArgumentException"])
        NSLog(@"Woops");
    else
        @throw;
}

However, this is very fragile since there's no guarantee that you're catching only the case in which the selector doesn't conform to UIAppearance.

Gabriele Petronella
  • 106,943
  • 21
  • 217
  • 235
  • 1
    Ok, you did point out that it's an awful suggestion, but its fine without the exception block. Don't swallow exceptions - let it crash and fail fast and let the developer know that it doesn't work. It's the next best thing for this situation than the compiler telling the developer it doesn't work. – bandejapaisa Sep 28 '13 at 09:35
  • 1
    Same thing I wrote under you answer. The point is to perform a runtime check, not to detect which methods are supported by the proxy. For that we have the documentation and the headers. – Gabriele Petronella Sep 28 '13 at 16:01
1

The best bet seems to be

  1. Make sure that the property in question is defined with UI_APPEARANCE_SELECTOR in the header.
  2. In the code test against the class UINavigationBar itself rather than its appearance

     if ([(some UINavigationBar object) respondsToSelector:@selector(setBarTintColor:)])...
    

or, as another answer suggests,

if ([UINavigationBar.class instancesRespondToSelector:@selector(setBarTintColor:)])...

While theoretically proxy is an object of a different class, the appearance proxy seems to be automatically generated by Apple from methods labeled as UI_APPEARANCE_SELECTOR. As the comment in UIAppearance.h states:

To participate in the appearance proxy API, tag your appearance property selectors in your header with UI_APPEARANCE_SELECTOR.

This method will break if you implement it for a property that was available through appearance proxy in the old version of iOS but was removed from the proxy later. Exactly one such example is documented in the same file:

On iOS7 ... the tintColor is now disallowed with the appearance proxy.

So you do need to test for rare cases to make sure the code is not broken by the new iOS, but you're still free of having to code the version numbers manually.

ilya n.
  • 18,398
  • 15
  • 71
  • 89