I want to know what the risks are of using undocumented methods in the iPhone SDK.
I haven't had any issues thus far, but I am worried that doing something like this might screw up the app.
Any opinions/suggestions are greatly appreciated.
Apple has recently begun running apps through a static analyzer that will catch use of undocumented (i.e., private) method calls and automatically reject them (unless Steve Jobs takes pity on you; I wouldn't count on it, though).
That said, there's nothing stopping you from using them for private apps, although you should be careful with OS updates since private methods are not guaranteed to function the same (even if you get the same results, the implementation might have different side effects).
The risks of calling an undocumented method are the same in any application; since the method is not publicly known, it is more likely to change without warning in a future version of the library than in a public API.
Related
I've read several articles about this, and I just want to see if I understand this correctly:
Apple will reject your app if you use a Private API...
What is the main difference between a "Private API" and a "Non-private API?"
Are the "Non-private" APIs only the APIs provided and verified by Apple?
Isn't an API just a way of interacting with a Framework, and a Framework is just a set of encapsulated classes/headers that people can use for trivial purposes?
Wouldn't this mean that I cannot reuse anyone's code (other than Apple's) at all in my app?
If this is true, whenever a programmer makes his or her own custom classes and methods, isn't he technically designing a mini Framework just for his specific purpose? So wouldn't this mean that he is using his own private API?
What would even be the difference between using someone else's classes/methods alongside my classes/methods, how could they even tell the difference, and why would this be a problem?
My understanding of this sounds absurd, and don't believe I am understanding what a "Private API" is correctly. I did read that it was to protect against any changes in the API that could render your App dysfunctional. Does this mean APIs are linked during run time (as opposed to compile time) and can be updated automatically without you knowing or something? (See, I was originally thinking of APIs/frameworks as being set in stone whenever you compile, maybe this is where I am wrong)
Can someone please help me out? I'm really confused about this policy. Thank you!
Q. What is the main difference between a "Private API" and a "Non-private API?"
Private is one that isn't publicly defined (there isn't much to it)
Q. Are the "Non-private" APIs only the APIs provided and verified by Apple?
Private and Public APIs are both 'provided' by Apple but only public APIs are provided for you to use.
The private APIs are for use by Apple only
Q. Isn't an API just a way of interacting with a Framework, and a Framework is just a set of encapsulated classes/headers that people can use for trivial purposes?
Yes
Q. Wouldn't this mean that I cannot reuse anyone's code (other than Apple's) at all in my app?
No
Q. If this is true, whenever a programmer makes his or her own custom classes and methods, isn't he technically designing a mini Framework just for his specific purpose? So wouldn't this mean that he is using his own private API?
No (a framework is different to an app)
Q. What would even be the difference between using someone else's classes/methods alongside my classes/methods, how could they even tell the difference, and why would this be a problem?
It usually wouldn't
The point you need to get is that when referring to private APIs you should only be thinking about private Apple supplied APIs.
Other APIs from 3rd parties are different (and Apple doesn't care so long as that 3rd party code doesn't use Apple private APIs).
For instance, I subclassed UILabel and added a method or property called -verticalTextAlignment to align text vertically.
And in the future, if next version of SDK or iOS added a method or property which has the same name, then it is possible my app crashs or behaves unexpectedly.
*This problem happens even if you use category instead of subclassing.
Question1
How to avoid this kind of accidental overriding in Objective-C?
I think you can avoid this by prefixing all of your methods and properties like -XXX_verticalTextAlignment. But it is not realistic, is it?
Question2
I know that this kind of accidental overriding happens at compiling time or when you update your iOS SDK, OSX SDK, or XCode.
But is it possible to happen also when updating your iPhone's iOS version?
For example, is it possible that your app runs well on iOS5 but doesn't run on iOS6 due to accidental overriding in iOS6.(You can replace iOS5 and iOS6 with any versions, for example iOS5.0 and iOS5.1)
Yes you could use your own prefix, however that is uncommon.
Yes, it can happen after an O/S update; Objective-C is a very dynamic language where messages are sent to methods rather than being called, as in other languages. These things are worked-out at runtime, not compile time.
Bottom line is yes, you might accidentally override a future base class method and the only solution is to be there when it happens and rename the method.
Prefixing your category methods are the safest way to avoid these conflicts in an every growing, non namespaced cocoa ecosystem.
It's quite valid and reasonable that framework creators, open source developers and other individual developers should prefix their class methods.
Quite often I write methods prefixed with my company initials, then continue with the method.
- (void)JCMyMethodNamedSimilarToBaseClass;
Question 1a Category
Use a prefix, or avoid the whole mess and use functions instead.
Question 1b Subclass Methods
If a method does something general enough that the superclass may also implement it, I simply choose a method name which is a little more 'wordy' than Apple would conventionally choose -- such as specifying a non standard typename in the method's name. Of course, this only reduces the possibility of collision.
If you need a higher level of security, you could just test this at execution (so you know when they are introduced) and hope every user stays up to date -- or you could rely on C and/or C++ more heavily (they do not have this problem).
Question 2
But is it possible to happen also when updating your iPhone's iOS version?
Yes. It's not so unusual. When the frameworks are updated (e.g. via software update), they may contain updated frameworks. This code is loaded into the objc runtime, and you always get the version of the installed frameworks' objc implementations.
It's also a much broader problem on OS X, where you may load code and/or plugins dynamically quite easily.
An elegant solution is to check for the method being already there and only adding it if it isn't: Conditional categories in Mountain Lion
I'm currently developing iPhone applications that -- for the time being -- need to be supported on versions prior to 4.x.
When I want to use something 4.x or 3.2 specific, I would use the usual approach of checking if a given class exists, i.e.:
if(NSClassFromString(#"SomeClass")) {
// do stuff here
}
This mostly works, except when it doesn't.
Let me explain:
Classes like:
UIMoviePlayerViewController (only available on 3.2+ according to the documentation)
UIGestureRecognizer subclasses (like UIPanGestureRecognizer; UITapGestureRecognizer, etc.)
Actually resolve to valid classes, as if they were already available on private APIs on earlier versions.
Is this the case, or am I missing something?
This is really bad, since the NSClassFromString() approach is actually recommended by Apple.
To be on the safe side, I'm currently checking the OS version instead of doing runtime checking for the classes existence.
Anyone else having this problem?
Thanks.
Nope, you are not doing anything wrong, there are a number of private classes that were made public in 3.2/4.0. This would not be a big deal if they were just exposing existing functionality, but often the classes also have significantly altered functionality.
The only 100% viable thing to do is to test the version, even though that is generally frowned upon. Even testing for specific selectors may not be sufficient, since there is no guarantee that the selectors on old versions behave the same as the selectors on the version where the object was made public.
Checking the OS version is even better than using NSClassFromString(...) since some classes already exist in versions prior to 4.0/3.2 (as you mention). You can just use this way :)
For MPMoviePlayerViewController, I use [MPMoviePlayerController instancesRespondToSelector:#selector(view)]. They were added at the same time and are part of the same framework.
Sometimes Apple documents this; e.g. for UINib I think you're supposed to check [UINib respondsToSelector:#selector(nibWithNibName:bundle:)]. I can't remember where I read this.
I haven't actually come across the recommendation to use NSClassFromString(). I usually use something like [AVCaptureSession class], but you can equally just use [[[AVCaptureSession alloc] init...] autorelease]. Nonexistent weak-linked symbols become NULL at load time; this means the AVCaptureSession Class-object becomes Nil, and methods on nil do nothing and return nil.
If you're going to compare version numbers, a somewhat more reliable way is to compare against kCFCoreFoundationVersionNumber. I don't see one for 4.0 yet; kCFCoreFoundationVersionNumber_iPhoneOS_3_2 appears to be the highest one in the 4.0 headers.
Anyone who develops knows that code is a living thing, so how do you go about defining a "complete" interface when considerable functionality may not have been recognised before the interface is published?
Test it a lot. I've never encountered a panacea for this particular problem - there are different strategies depending on the particular needs of the consumers and the goals of the project - for example, are you Microsoft shipping the ASP.NET MVC framework, or are you building an internal LoB application? But distilled to its simplest, you can never go wrong by testing.
By testing, I mean using the interface to implement functionality. You are testing the contract to see if it can fulfill the needs. Come up with as many different possible uses for the interface you can think of, and implement them as far as you can go. Whiteboard the rest, and it should become clear what's missing. I'd say for a given "missing member", if you don't hit it within 3-5 iterations, you probably won't need it.
Version Numbers.
Define a "Complete Interface". Call it version 1.0.
Fix the problems. Call it version 2.0.
They're separate. They overlap in functionality, but they're separate.
Yes, you increase the effort to support both. That is, until you deprecate 1.0, and -- eventually -- stop support.
Just make you best reasonable guess of the future, and if you will need more create second version of your interface.
You cannot do it in a one-shot release. You need feedback.
What you can do is first make a clean interface that provide all the functionalities your library should provide; then expose it to your user base for real-world usage; then use the feedbacks as guide to update your interface -without adding features other than helper function/classes- until it starts to be stable on the interface.
You cannot rely only on experience/good-practice. It really helps but it's never enough. You simply need feedback.
Make sure the interface, or the interface technology (such as RPC, COM, CORBA, etc), has a well-defined mechanism for upgrades and enhanced interfaces.
For example, Microsoft frequently has MyInterface, followed by MyInterfaceEx, followed by MyInterfaceEx2, etc, etc.
Other systems have a means to query and negotiate for different versions of the interface (see DirectX, for one).
I did some searches and found that if want to adjust the brightness, I need to use private framework and apple will reject it.
If I just want to get the value of brightness(just get that value to display, not adjust it), is there a way which apple accept?
Using a undocumented API of a public framework is not necessarily bad. I know many people, including myself who use undocumented methods here and there, for example to check for connectivity or add text fields to UIAlertViews.
On the other hand, linking to a private framework is much worse, because those tend to be very volatile, and might be removed from 3.0, or renamed.
I'm not sure about the specifics of your case, but the distinction between private framework or undocumented APIs of a public framework is important.
There is no way to answer the question "will Apple allow it?" You have to pick your risk tolerance and submit. Personally, I stay away from things that are undocumented. But in the cases that I do enhance my programs with things on the edge (generally undocumented things that are not actually private), I try to make my program resilient to changes.
One major way to do this is to check whether the thing you're about to link to or function you're about to call exists. For methods, you can use -respondsToSelector: before calling it. For functions, you can test whether the function exists:
extern void SomeFunction() __attribute__((weak_import));
if (SomeFunction != NULL) {
SomeFunction();
}
(You'll need to test that out on iPhone; I use it on Mac, and weak_import should be portable since iPhone uses GCC4. See Ensuring Backward Compatibility. So far, I've always been able to find some trick to avoid actually needing this on iPhone.)
The key to all of this is to make sure your program functions without the undocumented feature. That way, even if Apple rejects it, pulling it out is trivial.
That said, for me this is a last resort for things I've spent a lot of time trying to do in a documented way, for things that provide a significant improvement to the user experience, and don't violate Apple's basic intent. For instance, running in the background, even if you can get it to work, is a clear violation of the spirit of Apple's rules; other things are private just because they haven't been made public, like the internal views of UI elements.
Changing the screen brightness in a way that persists outside your app would seem a violation of the basic rules of play (stay in your sandbox). Getting the current screen brightness doesn't sound like that (though I'm not sure what you would do with it.)