I am talking about what I learned by designing APIs for various frameworks and internal modules and my experience in making APIs more robust and how to prevent errors before they ship to the user.
• Class • Group of classes • Modules • Everything you see from the outside Higher Level API Class Category Method Method Category Method Method Class Category Method Method Category Method Method
a straight forward implementation • Often these two are interconnected, but if not, choose the better API, not the better implementation • Design your API based on use cases and think about how an ideal API would look like for the use cases in mind • Don’t start with what you have and try to build a nice API wrapper around it
• These layers may have different consumers • Consider these groups when designing your APIs • A private API might not need to be as rock solid as a public API • It may be okay if some helper method, private to a class, has a horrible API to speed things up, but is it okay for a public method?
using an API might be different based on what the API is for • The newest animation hipster framework • An API to get a 3D model from image data in real time to analyze the traffic around an autonomous vehicle • If you think customer support is bad, you clearly never did any support on GitHub…
a long running, blocking task. Only call from a background queue! !*/ - (UIImage *)renderImage; vs /** Renders an image. !*/ - (void)renderImage:(void(^)(UIImage *))completionHandler
The less features the better • Only provide what you really need • Keep the interface of classes small • Group methods together • Think hard about the naming
together • Inside these classes, methods don’t have the render… prefix -[PSPDFRenderTask initWithRequest:] -[PSPDFRenderQueue scheduleTask:] Example: Rendering in PSPDFKit PSPDFRenderRequest PSPDFRenderTask PSPDFRenderQueue PSPDF Cache
!!... @end !// UIGestureRecognizerSubclass.h @interface UIGestureRecognizer (UIGestureRecognizerProtected) @property(nonatomic,readwrite) UIGestureRecognizerState state; !!... @end Example: Hiding as much as possible
Swift too! • -[NSString foo_myMethod] / -[NSString fooMyMethod] • If it is an Objective-C class under the hood, Swift will expose these things • If a framework’s class adds a method: • In your subclass it breaks your subclass • In a category it breaks every consumer of that method
!&& fileURL.isFileURL); _fileURL = fileURL; } • People will try to put a string or a remote URL in there! • Enforce things like this early to avoid trouble afterwards
Make properties for potentially mutable classes copy their values • Make all properties on a class readonly • Consider creating mutable / immutable pairs if necessary
make all consumers copy the class! • Homework: Use the Objective-C runtime to automatically create the implementation of a mutable / immutable pair including support for NSCopying, NSMutableCopying, and NSCoding Example: Readonly properties
of a class to a private queue • read concurrent, write serial dispatch_sync, dispatch_barrier_async • Consider prefixing internal methods: queue_ or barrier_ Makes it almost a no brainer when refactoring
and it is not absolutely clear on what queue a block is called: • As the maintainer of a public API: always call on the main queue • As the consumer of an API: always assume any queue • As the one writing the documentation: lie • “The block is called on an arbitrary queue”
method does internally • Document only what you want to guarantee • What you want to guarantee and what you do might be distinct • Use the wording that Apple is using
change • Try to prevent breaking changes whenever possible • But don’t cripple your API for that • Provider proper deprecation – most of the time it’s easy • Use semantic versioning Public APIs
of concerns, the problems are by far not as big as they look like • Keeping your APIs small helps to keep the focus of this particular API • Naming things properly helps the consumer of the API and your future self when refactoring things • Immutability helps with designing a proper information flow and it automatically makes your code thread safe • Breaking things into pieces makes things like thread safety much easier
your APIs are thread safe, immutable, small, simple, and separated you can refactor, optimize, change, iterate, test, and improve things very fast, easy and cost efficient because you don’t need to think – and thinking is where the shit hits the fan.