Skip to content
This repository has been archived by the owner on Nov 29, 2022. It is now read-only.

Safe to use Private Property for LongPress? #39

Closed
winkelsdorf opened this issue Nov 24, 2015 · 11 comments
Closed

Safe to use Private Property for LongPress? #39

winkelsdorf opened this issue Nov 24, 2015 · 11 comments

Comments

@winkelsdorf
Copy link

In configureBarItemsGestures you are accessing the private View property of the UIBarButtonItems. Seems to be the best way due to the lack of UIBarButtonItems exposing their gesture Recognizers.

Are you aware of any App Store rejections caused by this?

@dzenbot
Copy link
Owner

dzenbot commented Nov 25, 2015

I have used this library for several production apps, and never had any issues with it. Accessing hidden properties from objects isn't really that illegal; I've never had any app rejected because of doing it at least.

@dzenbot dzenbot closed this as completed Nov 25, 2015
@winkelsdorf
Copy link
Author

Thanks for the heads up! That's what I hoped to hear, so thanks for the confirmation :)

For other people stumbling upon this thread:

After years I never jumped into the water of accessing private APIs, sub-classing and in ObjC days sometimes method swizzling were all I needed. But at some point, like detecting long-press on a UIBarButtonItem for back/forward history, you have to rethink if there is another way. Welcome to the dark side ;)

Your solution uses KVO to access the view, which is probably the most elegant approach.

For reference this post titled "Five Pieces of Evidence for "It's Not Private" on SO sums it up, why I might not be that "illegal":
http://stackoverflow.com/a/11924449/844907

  • It's a property that you can get to in other ways. Try this and one of those views is, in fact, the _view ivar of the UIBarButtonItem in question. This indicates that access to this UIView is not prohibited itself, though the KVO way in might be questionable (but I doubt it).
  NSArray *array = self.toolBar.subviews;
  for (UIView *view in array) {
      view.backgroundColor = UIColor.greenColor;
  }
  • They actually trigger the KVO for this property. ivars do not have to trigger the KVO API, right?
  • @farcaller mentions a similar case which is for sale in the App Store. Since he/she answered within the first 20 minutes of the question being up there, it's reasonable (but not safe!) to assume that there might be thousands of apps in the App Store that do this.
  • This UIView gets subbed out each time the button is pressed, so you cannot just, for example, set a gesture recognizer on it and be done. You can, however, keep setting the same gesture recognizer every time the view gets replaced. To me, this is actually more evidence that it's not a private API thing, but rather you have to be very careful when using it (and use KVO to make sure you have the latest one).
  • My app is for sale in the App Store and does this.

One could argue if this approach needs more work when speaking of the current implementation in DZNWebViewController, like observing the KV for view and re-inject the gesture recognizer as Yar pointed out on SO, but currently the only thing that would happend without is, that the gesture would simply not work. Nothing spectacular.

@dzenbot Thanks for the nice solution!

@farcaller
Copy link

Mind that I answered it like three years ago and the Apple policy on that changed. Still, I guess the code in here is reasonably safe.

@winkelsdorf
Copy link
Author

@farcaller Thanks for joining! Absolutely, many things have changed. But agreed, the approach taken is still valid.

If someone is really picky about: with Swift 2 a simple do-try-catch sandwich around the exchange will ensure that accessing this private View has no consequences if Apple decides to make some changes in future.

@farcaller
Copy link

Nothing swift-specific there, you can have @try block in ObjC for the same purpose (you should actually have it there, as that's common sense for using any unstable api).

@winkelsdorf
Copy link
Author

Right, I'd been working through the changes of Swift 2 in the past days, and mixed that up.

That's probably because the ObjC @try block was very rarely used in projects I saw in the past years. Very rarely. Instead of the slower try blocks with their encapsulation overhead you would likely have seen people checking for class functionality (selector checking), that's what is best practice anyway when not dealing with unstable (external) APIs but the probably changing ones from Apple.

Anyway the exception handling changed when compared to objc.

@dzenbot
Copy link
Owner

dzenbot commented Nov 26, 2015

I guess we could do something like this instead:

if ([backwardButton isKindOfClass:[UIView class]] && backwardButton.gestureRecognizers.count == 0) {
        // interact with the gestures safely
    }

This way, we're 100% sure we're dealing with an UIView instance which responds to the gestureRecognizers selector. valueForKey: is totally safe since it just returns nil without exceptions if the object doesn't respond to the selector.

I personally avoid @try @catch wrapping as much as possible, not only because I also believe it's an overhead, it's just not flexible enough. Checking for class existence [ThisClass class] or check for respondsToSelector: is usually good enough and backwards compatible.

@winkelsdorf
Copy link
Author

@dzenbot Fully agreed regarding the best approach where available. But I am pretty sure that isKindOfClass would not work here: The UIBarButtonItems do not subclass UIView. They are subclassed from > UIBarItem > NSObject. The UIView is just a private (Sub-)View of the Buttons that's inaccessible without KVO or iterating through the Views.

So the current approach seems still to be the best.

@dzenbot
Copy link
Owner

dzenbot commented Nov 26, 2015

This check is not for the UIBarButtonItem, but it's private UIView property optained using valueForKey: aka KVC (not KVO)
Snippet here

@dzenbot
Copy link
Owner

dzenbot commented Nov 26, 2015

That view property of UIBarButtonItem is actually an UIButton instance really, which inherits from UIControl/UIView

@winkelsdorf
Copy link
Author

Both is right. KVO came from having my current KVO code for WKWebView in mind. Ok, time to get some sleep, it's in the middle of the night here ;)

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants