API Design for Cocoa and Cocoa Touch

Session 138 WWDC 2010

Cocoa and Cocoa Touch feature highly consistent design patterns throughout their APIs. A pattern used in one context is readily available in many other contexts. Build better apps more quickly by understanding these patterns along with the motivations behind their design. Learn about API conventions and guidelines, techniques for extending Cocoa classes, exceptions, errors, performance, thread safety, among other common Cocoa API design patterns.

Ali Ozer: Good afternoon, everyone.

Welcome to API Design for Cocoa and Cocoa Touch.

My name is Ali Ozer.

I'm the Manager of the Cocoa Frameworks team at Apple.

OK. So first, there's lot of you here, let's just make sure we're on the same page with regards to what we mean by API.

Now, I looked it up.

API turns out to have many meanings.

But we're not here talking about Active Pharmaceutical Ingredients.

We're not talking about Armor-Piercing Incendiaries.

Definitely not the American Pirate Industries.

And it's not the American Pain Institute.

Now, these all sound like fun topics for talks, but this is not what we're covering.

Instead, we're talking about Application Programming Interfaces.

Now, APIs, Application Programming Interfaces, are very important because they allow your code to interface with the system, you know, let you use the facilities of the operating system.

But well-designed APIs go much further.

They make you more productive.

When you're using well-designed APIs, you don't have to look at documentation for every single API.

You've learned how the APIs work in general.

You can predict how they're going to behave.

Well-designed APIs allow you to leverage existing code.

Not only can you reuse something, you can go ahead and extend it to your own needs.

Well-designed APIs also let your write code that works as the user expects.

That's because well-designed APIs set policy.

And by using these APIs, you make apps that, for instance, do the HI guidelines correctly, so the user is happy because your code works like other code.

Now, APIs live for a long time so that's why we consider API design very important.

Once we've put an API out here and it's adopted, it's very hard for us to get rid of it.

So, if we made some mistake, it takes a while to correct that mistake.

And one final factor is that API design is UI design for developers.

So, just like Apple takes UI design of its software and its hardware very seriously, we also take API design very seriously.

Here are the five factors we consider important when we're designing APIs, and the talk is going to cover, it's going the talk is going to have these five sections covering these areas.

Each section is going to start with why these areas are, these factors are important, and now I'm going to give examples of how these factors are reflected in our APIs, and how you yourself can also use these use these factors in your own APIs as well.

Now, this talk assumes knowledge of Objective-C, and you've hopefully programmed a bit in Cocoa or Cocoa Touch.

A lot of the examples I'm going to use are from Foundation, since it's a common substrate to AppKit and UIKit.

However, in some cases, we'll also use examples from AppKit or UIKit as well.

OK. So with that, let's begin to our first section which is consistency.

And the main topic in this section is naming conventions.

Now, I'll just cover some of the naming conventions we use in our APIs.

Now, why is consistency important?

Well, as I said earlier, it eliminates the need to refer to documentation for every single API.

You can read about the general guidelines, the general rules, the general philosophy, and now you don't have to look up every single API to understand what it does or what other APIs there might be with it.

Consistency also allows different subsystems to plug-in to each other more easily.

You know if you get one Objective-C API from someone and another one from someone else, with this consistency, it should work well together.

And finally as, well not finally because there are many, many reasons why it's important, but one other way it's important is that being having consistent APIs actually can improve performance as we'll see later.

OK. So let's dive into naming conventions.

And the first thing I want to talk about is naming of classes.

I'm sure many of you have seen some of the classes we have in our APIs: NSString, UIView, CLLocation, et cetera.

And typically, class names are nouns.

And one thing you'll notice is that we use prefixes.

Now, prefixes protect against collisions, and they also differentiate functional areas.

A name like view or location is highly generic.

It's possible that two different developers might use the same name.

But in addition, using the prefix like CL allows you to immediately recognize that this class comes from Core Location.

So in your own designs, in your application, you don't have to use prefixes since you're managing the whole namespace, but it might be a good idea for you to use prefixes as well in which case I recommend either using three-letter prefixes or maybe even more complete longer prefixes for your class names, where it may be it's the name of your company.

Method names, you've undoubtedly seen many methods if not before during this week.

Here are some examples.

With method names, we focus on readability.

For instance, the name "is Editable" there.

We put the "is" in there as a general rule because it makes it easier to read, myTextField.isEditable.

That reads very naturally.

Now, some other things you'll notice here.

We use camel case.

What camel case means is the subwords inside our names are actually capitalized.

We don't use underbars or anything like that.

We actually use uppercase letters.

And one other thing you might notice is we choose clarity over brevity.

In some cases, our names are not short and we also name all the arguments.

Now, I'll talk about these last two now.

So, here's an example method from NSMutableArray removeObjectAtIndex.

You might be wondering why all those words.

Well, this method is trying to communicate to you that removes an object at an index, so the argument is an index.

In fact, we have another method called removeObject which removes an object.

So you can immediately see that these two are distinguished by those words, and it's really clear what each one does.

So, those of you wondering why isn't there just a method called remove, well you can now understand why that the method remove would be ambiguous in the presence of these other two methods.

Remove by itself just does not communicate either one of these.

And for that reason, we often try to name, you know, with a noun what the argument is about.

Naming all the arguments.

Here's a method that's not the longer method names in our frameworks.

It comes from NSBitmapImageRep, in AppKit.

You can see all the keywords I'm talking about, you know, the pixelsWide, pixelsHigh and so on.

Now, here's a secret, but I don't want you to take it out of this room.

In fact, you should forget about it after the beer bash wil help with that.

I want you to forget about it after you see it.

But you can create a method name like this where you actually removed all those keywords.

But we don't want you to do that.

As you can see, you immediately start losing, you know, some of the what you're trying to communicate.

When you're using this method in code, here's what this looks like.

You know, you have all these numbers like 32 YES, NO, 0.

Without those names, you know, really this will be lot less clear.

In fact, let me give you a concrete example.

Back about year 2000, we're experimenting with Java and, you know, a lot of other people were a lot of crazy kids.

But we didn't inhale.

[ Laughter ]

[ Applause ]

Anyway, so here's what this and we have this class method actually existed in Java as well.

Here is what it look like.

New NSBitmapImageRep, and as you can see, you immediately lose all what all those arguments mean - 32, true, false you know, you have to go and look up what this means.

So, this is the reason why those arguments are important.

OK. Naming accessors, getters, and setters.

Here are some examples:- color, setColor, isEditable, setEditable, so on.

Now, one thing to note, as I mentioned earlier, for boolean properties that are adjectives, we use "is" on the getter.

So, it's "setEditable", but the getter is "isEditable".

Another thing you might notice is that we do not embellish the getter with "get" or other verbs.

So, for instance, the color method is not called "getColor".

It just reads better to have it be "color".

Another example is we don't put words like "compute" on the accessors.

For instance, the last one here, even if the thumbnail image is computed, you know, when you ask an object for its thumbnail image, even if that object computes the thumbnail image, it's usually not interesting to communicate the fact that it's being computed.

Just have the property, call it ThumbnailImage and that's good enough.

Only in the cases where it is important to communicate that or distinguish it would you want to put those words in.

Now, there are some acceptable uses of "get", and that's one case is when we use, we use get on accessors that return values by reference.

For instance, the NSData method getBytes: which returns an arbitrary number of bytes given the range argument, it really needs a buffer so to get method.

Another example is this method from UIBezierPath which returns three related arguments, three related properties at once.

In this case, we actually have a get method that returns three different values.

They are all by reference.

You'll note that the caller can pass NULL in for the arguments they don't want in such cases.

For accessors, the simple accessors, you can certainly go ahead and use @property and we encourage that whenever it's possible.

You know, our accessors in this case would look like this, property (copy) UIColor *color.

For the editable one, we have a special getter because @property does not know about our rule about putting "is", so you might have seen this in our APIs where we specify a special getter.

Now, let me talk a bit about functions.

Function names you probably seen a few.

Here's what they look like.

Typically, for functions, the rule is to use a framework prefix followed by the type or functionality area, and then followed by whatever is happening.

For instance, CFRangeMake, CGPathAddLines.

And the common prefix allows easier searching, sorting, et cetera.

Enum values and constants are very similar.

Here are some examples.

Now, we also have sometimes common suffixes.

Notification is one of them.

We use that for NSNotification contants.

Key is another one.

That's for symbols that are dictionary keys.

Now, one thing to note about functions, enum values and constants, and it's actually true for some other APIs, our naming conventions have evolved over time.

So, although we have some examples of, you know, great naming, which follow our conventions, you will also occasionally run across names that are not that great.

These are mostly some of the older names that we haven't either had a chance to deprecate or we're just leaving it in there for old time sake.

You can see some examples here.

So, not everything you run across especially in some of the older APIs may be correct but, you know, in general, for new APIs, we definitely follow the latest conventions.

Do not abbreviate arbitrarily.

Here is a fine-looking method, which is maybe a little too wordy, but you know, as I said again, there is value in that.

You might make it just a little bad by arbitrarily abbreviating point to pnt, you know, setFloatingPntFormat, OK.

I mean this generally help you because Xcode usually helps you at typing.

Or you can make it really ugly by removing all the vowels out of it, if you're a vowel hater.

But you know, we don't recommend this.

So, the first one is really the best one here.

Now, there are some acceptable abbreviations though, and it's good to be consistent.

Our documentation lists some of these acceptable abbreviations: alloc, dealloc, et cetera.

There are also commonly used acronyms such PDF and USB, and we recommend you use these.

Because if you went ahead and spelled out portable document formats, some people might not even know what you're referring to.

You know, people are so used to terms like USB and PDF, so it's best to stick to them.

It's also good to stick to consistent terminology.

Earlier, I gave you the example of remove, you know, maybe one possibility is, hey, instead of remove objects, let's use delete, and instead of remove this way, let's use some other term.

So, you know, there might be a tendency to look in the source and come up with synonyms.

There are a lot of great synonyms.

You know, vaporizeObjectsAtIndex, sounds great.

[ Laughter ]

However, it really does make APIs less predictable, so it's, you know, good to stick to consistent words and use more descriptive words to describe your functionality.

And avoid names that are ambiguous.

You know, at first glance, these names don't look so bad, but sendPort, is that a method that sends a port or is that a port used for sending?

displayName, is this a directive to display a name or is it name used for display?

Center, is that a verb or is it a noun?

It turns out center is a verb in AppKit and a noun in UIKit, so there's one we missed out of the gate.

So, it's good to try to disambiguate these names and choose a little more clear names.

Here are some suggestions here.

One is this portForSending.

localizedNames is actually one we do use whenever we can.

And a middle, maybe center as a noun, middle might be a better term, we don't use it, but maybe we should consider.

And let me talk a bit about Block naming.

Blocks, as you know, are new in Snow Leopard and iOS 4.

Block itself can be ambiguous because Block as a verb is like block, you know stop, block processing.

So, we try to be careful with that.

So, we use the term Block only in generic cases such as enumerateObjectsusingBlock.

But in other cases where we can have more descriptive names, we have come up with a list of names that we use.

For instance, indexOfObjectsPassingTest, a Block that returns a boolean that cites or testing is a test.

We also have Comparator, handler, completionHandler, ExpirationHandler, et cetera.

So, these are some of the terms we are using to describe blocks.

OK. So, the last item I want to discuss in this section is object ownership.

Now, most of you are probably familiar with object ownership rules in Cocoa and the memory management rules.

Object ownership is not transferred across calls, and the only methods that actually do are exemptions to this rule are methods whose names begin with alloc, new, copy.mutableCopy or the retain method.

So, what this means is whenever you're looking an API, you never have to ask the question, do I have to release the results from calling this method.

And similarly, if you're designing your own API, you have a good guideline.

Now, there might be mistakes in APIs especially if a subsystem was designed without API guidelines in mind, maybe it's an internal subsystem of your application.

And if you're on the Clang Static Analyzer, it will mark these erroneous names and give you warnings that you're over-releasing an object or you're not releasing it enough.

We do have some attribute, a decorator you can add to methods for this purpose.

For instance, if you had a method name, full name that erroneously returned and retained object, you could put NS_RETURNS_RETAINED on there to sort of describe to the analyzer, hey, this returns your retained object.

However, please use this only for Static Analyzer's purposes.

You know, do not use it to define bad APIs or just iffy APIs.

We intend not to have any APIs, you know, in this system that use this, for instance.

If we did, that would be an error and we'd want to fix it.

OK. With that, let's get to the performance section.

In here, we're going to talk about Impedance matching, I'll describe what I mean by that, mutability, and concurrency.

So, first of all, why is performance important?

Well, you know, it seems like that's an obvious question.

Users love it, you know, when the applications are fast in response, so that seems like a clear one.

But another reason performance is important is that even though an application might seem responsive and fast, maybe it's not fast enough or maybe it's not doing things fast enough where it's using more power than needed.

So, being especially focused on performance, you know, making sure you're getting everything as fast as possible can improve battery life and make users happier.

So, let me talk about impedance matching.

Impedance matching, to describe what it means, it's a term, I think, from electrical engineering where it's talking about input and output connections, and it's done in such a way that the power is maximized, the throughput is maximized.

So, it's basically a term that, you know, where you can connect things together with highest efficiency.

You might think of it as plug and play, really, easy plug and play.

Now, what I mean here is this concept of using small number of basic data types in APIs, and not having redundancy.

That improves consistency.

You can plug things together more easily.

Code fits together more easily.

In addition, it eliminates the need to do conversions.

If you have two different representations for the same concept, every time you cross an API boundary, you have to do conversions on those, and that just reduces performance.

So, for to this end, we have a small number of basic data types for strings.

You know, we use NSStrings everywhere, in all API for strings.

NSDates for dates.

NSURLs to represent file paths.

For ordered collections or association tables or map tables, we use NSArray, NSDictionary.

There's also NSSet, but it is used less commonly in APIs.

In addition, we have this other higher level types, UIColor and UIKit, NSColor in AppKit, and types for font and image as well.

So, I recommend that anytime you want to represent an image in your application or in you API, you use either UIImage or NSImage with regard to UIKit or AppKit, and there are few other types that fit here.

Now, not all basic data types are objects.

You might know that we have some C types in our APIs as well.

For instance, NSInteger and NSUInteger.

These are actually simple typedefs on top of int or long.

CGFloat is another type like that.

It's either a float or a double.

Now, we do have an NSNumber type.

It's an object to represent numbers.

But you've probably have noticed that we do not use NSNumbers in our APIs to represent numerical quantities, we only use NSNumbers to wrap these basic types when necessary, for instance, when you're putting them in dictionaries or arrays.

Now, we also have some struct types.

And for widely use types, you know, things they are using all over the place where abstraction is not important, it's not valuable, it's OK to use structs.

We have four widely use structs, points, ranges, sizes, and rects.

Consider this point, you know, CGPoint, it's got X and a Y.

We have, you know, we do not pretend that anybody will want to extend CGPoint, nobody is going to want to subclass CGPoint.

We have a bunch of utility functions and that's good enough.

It's OK for you to go in there and group the X and the Y values, you know, it's OK.

It's a public struct, we all understand it, it works well, points aren't going to change.

With that in mind, structs are OK.

Structs are good because you can, you know, stack allocate them, and you can use them, you know, pretty flexibly.

Now, once upon a time when we were designing some of the basic types, we made the mistake of designing the color type to be a struct.

So, it was basically a struct with RGBA, and we quickly realized our mistake, you know, and color is much richer type than that.

You know, you have different color representation, CMYK.

You have color matching information.

So, we could quickly dump that idea and switch to a color object.

So that was an example where our struct is not OK.

Now, so far, I've been start talking about reducing a number of types, you know, et cetera, but then I'm also talking about sometimes multiple types that seem equivalent and that seems to violate what I'm saying.

So, you know, what's up with CGPoint and NSPoint?

Why do I have two types to represent points?

Well, those of you working on UIKit probably have never even come across NSPoint because that's an Appkit concept.

NSPoint was the original type for NSPoint in Appkit.

CGPoint was added later.

In fact, we have made the two equivalent in our 64-bit by making them the exact same typedef.

So, for all purposes, these two should be considered equal when you're working in Appkit.

And in UIKit, you only have CGPoint.

How about NSInteger and NSUInteger?

These represent signed and unsigned integer value.

Why do you we have these?

Why don't we just use int?

Well, our API is used to all the in terms of ints or unsigned ints, but then when we moved to 64-bit on the desktop, we decided to add these typedefs to sort of abstract that away.

So, all our APIs are not in terms of NSInteger or NSUInteger, and these are defined to be int or long as needed by the platform.

So again, these are just basic types on those and they are just simple covers.

And finally, the CFStringRef versus NSString.

Some of you might have played with Core Foundation, used the CFStringRef type for instance, and you might be wondering why do we have two types for strings.

Well, it turns out these two types are actually totally equivalent.

You can just typecast between them, no conversion is necessary.

The reason we have CFStringRef is to satisfy the needs of very low level implementations that do not use object-oriented programming.

For all object-oriented implementations based on foundation or above, use NSString.

It plays much better with the object-oriented environment.

And it's also on the desktop, especially.

This is important.

It's garbage collection ready out of the box.

So, we refer to this relationship between these two types as toll-free bridging.

And this relationship also exists for CFArray and NSArray or CFDictionary, NSDictionary and also a few other types.

OK. So with that, let me talk about mutability.

Now, mutable means changeable, sort of like the term "mutate."

Many objects are by nature mutable only mutable, for instance, NSWindow or UIScrollView.

It's got many properties that can change, and it's a changeable object, you can't even think of it as an unchangeable, immutable object.

But then there are other types such as strings or colors that can be considered in immutable forms and these are usually value objects.

And what I mean by a value object is object whose important characteristic is its value not its object identity.

If you have two strings and their contents are both "hello", you would consider those two strings to be equal, right?

So, those are value objects.

And in those cases, the immutable approach makes sense.

And in some cases, having both a mutable and an immutable variant also make sense.

An example is NSString.

If you have an immutable NSString, here is how you would append to it, you have your string, you call stringByAppendingString and you get back a new string that has a result.

If you have an NSMutableString and you want to append to it, you would just call appendString and it just modifies that sting in place.

So, looking at that, you're like, "Hm.

Mutable string seems more efficient here.

We don't have to create an intermediate object.

Why do we even have the immutable version?"

Well, there are some reasons and actually performance is one of them.

If you have a string which has five characters in it like hello and you know it's never going to change, the storage for that string can be very optimized.

It can store those five characters in its base object.

In fact it doesn't even have to create another malloc block for instance.

This also yields to simpler implementation.

No mutation is needed.

You don't have to keep track of all sorts of editing state.

This yields to thread safety.

You know, if you're not changing, you can be a lot more thread safe.

And this also allows for easier analysis of program logic.

If you have an object at point A whose value is hello and it's immutable, you know that later at point B, it's going to have the same value, so that makes some things about your program easier to understand.

OK. Now, which one to use in APIs?

For instance, let's say you're a window or a button and you have a title, here's what your API could look like.

NSString title to give you a new title, you know, the callers exactly give you a new title.

Or you can have an API like this, NSMutableString title where the expectation is that if somebody wants to change your title, they basically reach in there and mutate that string.

As you probably realize, this latter is not what we'd like.

We almost always recommend the immutable version; that's because sort of having immutable title and reaching in there to change it feels like you're totally breaking the encapsulation of a window or a button object.

However, oh, and by the way, this is if you're using properties, which as I said is usually, you know, good idea, this adheres to the way how you would declare this property, property (copy) NSString*(title).

Now, there are few exceptions to this.

For instance, there's an NSAttributedString class.

It has a string method, however, its subclass NSMutableAttributedString has a mutableString method.

And this method, just because it's so special, it returns NSMutableString value and has the word "mutable" in the name actually expects you to mutate that.

It's ready for you to mutate that.

So, this is, and in this case, when you mutate that string, the containing mutable attributed string, it sees the changes and takes appropriate action.

So, these exceptions are almost always going to be named with the word "mutable" in them one way or another to make it clear to you that yes, this is OK to mutate.

OK. Let me now talk about concurrency.

Now, concurrency is a way to achieve higher performance on multi-core machines.

As you know, on the desktop, we're adding more and more cores instead of cranking up CPU speeds.

So, being able to run things concurrently means you can achieve higher performance.

Now, we've you know, all along we have various ways to do concurrency we had thread objects, we had runloop objects, we had notification queues, and so on.

But now we have a way to represent even more granular, smaller grained work, and blocks are of course that tool.

Blocks are a good fit for presenting concurrent work.

They can be processed by Grand Central Dispatch.

They can be executed by NSOperationQueue.

But even more importantly, they can capture state.

Now, if you're going to have some concurrent work happening, it's great to be able to encapsulate that work, throw it out there, and have that block capture all the state it needs so it doesn't rely on you staying around anymore.

So, from that point of view, blocks are also good hit for concurrent work, but note that not all block usage is necessarily concurrent, and we'll see examples of this later.

Now, in our in the APIs we're adding, when we want to enable concurrency, there's often an explicit option, for instance, enumeration, sorting, searching.

Here's an NSArray method enumerateObjectsWithOptions:usingBlock.

Here's a usage case for it.

If you provide the option NSEnumerationConcurrent, the block is executed concurrently with elements in that array, so that's an explicit way to get concurrency out of enumeration.

If you're at the What's New In Foundation talk, Tuesday, you probably saw this in a lot of the other related APIs that we introduced in iOS 4 and Snow Leopard.

Here's another example, NSNotifications.

Observers can now indicate that they want to receive notifications concurrently.

For that, we have a new method, addObserverForName object queue usingBlock.

If the observer indicates a non-nil queue, the block will be executed concurrently for that observer.

So, this is again, an explicit way to indicate your find for concurrency.

And here's one more example out of the spell, NSSpellChecker class.

NSSpellChecker can do the spell checking.

This class does the spell checking, grammar checking, et cetera.

It can do the checking either in a synchronous fashion with this method where this method simply returns the result as its return value, which makes sense for a synchronous approach, and then returns additional results here in these two argument, or it can do it asynchronously with this method where the results are communicated in the completion handler block at a later time, and the results are returned in these arguments, passed to the block.

And also note one more thing, the sequence number passed to the block is actually the integer value that was returned by this method so that you can associate the request and the reply at a later time when the reply comes in.

OK. So, with that, we get to our safety section.

And here, I'm going to talk about runtime errors, programming errors, and also atomicity.

So, safety is important because, you know, you can reduce the chance of crashes with safer programming.

You can make debugger, debugging more easy, and also writing more robust apps, which even if errors occur, it can indicate to the user why errors occur then allow the user to recover is a good idea.

Now, there are two types of errors that we're going to talk about, expected errors which we also call runtime errors and programming errors which are sort of these unexpected errors that are caused by often bugs in the program.

Now runtime errors are errors that are expected to occur.

For instance unreadable file, a file gets corrupt, the user tries to open it, they get an error out of disc space, user is trying to save a file but the disk was full, lost network connection, invalid user input, you're trying to make a trade and you indicate billions instead of millions you know it might happen.

So these are the kind of errors that your program should guard against.

Now these are typically handled with return values in an optional NSerror.

The return value is often a boolean or an object that returns an initialized object or nil.

And in cases where you want to report an error you would use NSError argument, here's an example.

Here the ID returns, you know, returns in your object, if it returns nil then the error argument, NSError** that's a by reference argument indicates what the error is.

And of course as before you can pass NULL here if you don't want to hear about that error.

Now sometimes NSErrors can be passed by other means for instance here is a UIWebView delegate method, if an error occurs during loading, this delegate method is called and the error is simply passed to the delegate.

And here's another example: NSWorkspace.

This is a method to NSWorkspace to make copies of files just like the finder does on the desktop.

This is an asynchronous call in a way to you pass the URLs to copy and the completion handler is called with the NSDictionary indicating to you what the new names of the copied files are and in addition NSError which could be nil if no error occurred or maybe an error that indicates a partial error like 3 of the files could not be copied but the rest were.

So this is actually a pretty novel way of using and indicating a partial error.

Now not all APIs need NSError in fact if you look at our APIs you probably see only a few percent of them have NSErrors.

That's because NSError usage is really best confined to API's you know where you really want the caller to either look at the error and make decision based on the error type.

But even that's a chore you know having to look through the NSError, figure out the code, have a big switch statement and then in the future new codes are added you know that's not fun.

So the other, the more compelling reason to add NSError is that the error may be reported to the user.

What I mean by that is you get an error from an API.

You know, you can just put it up to the user and in fact have the error automatically provide ways of recovery from that error and NSError has that.

It has the failure reason which is localized and it's got recovery suggestion and even a way to provide recovery options.

And the errors we provide out of our frameworks usually have well localized error messages and sometimes recovery suggestions as well.

And if you're using the App Kit there's this responder API that will let you present an error either modally or as a sheet.

But on UIKit as well you can actually take an NSError and show the various localized pieces in whatever fashion you want.

OK. So let's talk about programming errors.

And these are errors often caused by misuse of APIs.

Here's an example.

This is out of bounds access into an array.

Here's another example where you're calling a method which expects a caller but you're calling it with a string, you know sort of maybe looks like a caller.

And an invalid parameter value, we often consider nil not to be a valid string when passed to APIs and you know this is a bad call here setStringValue of nil.

Now we do not indicate programming errors by return values because programming errors are really errors in programming in program logic and these should be fixed and dealt with before you ship the application.

It's not something that you'd expect to deal with on the user's machine at runtime.

You want to deal with it before you ship so you, you know, you test, you find these bugs, you fix them.

So there's no need to deal with them at runtime.

So therefore you will never see an API like this, setBackgroundColor, which returns an error code.

And if you happen to pass in a string instead, it will return an error indicating a string passed in.

I mean how do you deal with the set runtime is just like why would you do that, you just fix this bug.

In fact, the compiler will probably tell you that you're passing a wrong argument if you have a type checking happening.

So, not a good idea.

We indicate these errors via exceptions using NSException and it's important to note that in this fashion, Cocoa uses exceptions in somewhat different ways than some other frameworks than some other platforms do, so that is the difference in the way exceptions are used on our system.

And you know, we do not expect you to handle exceptions.

For instance, you shouldn't have to write code where you set a background color and then put that tri-cache block around it just in case a string was passed and so you can cache it or run deal with it.

Again. don't write this kind of code.

Just listen to the compiler and try to fix these bugs discover and fix this bug before you ship your application.

Now, it might still be a good idea to have a top-level exception handling in your application.

So if these kinds of exceptions do occur for the user, you know, you should be aware, hopefully.

You can alert the user something bad happened or you can try to recover and let them say now, if you write the Cocoa chips Tips and Tricks Talk on Tuesday at 2 p.m., you saw a technique a way of doing this on the in the context AppKit.

So, the last safety topic I want to talk about is atomicity.

Objective-C 2.0 properties are by default atomic but they can be made non-atomic by putting non-atomic keywords.

And many APIs in iOS, in iPhone OS are actually have non-atomic properties.

Now, here's what atomic does.

It guarantees that when one thread is setting or another and another thread is getting a value, you're guaranteed to get a good value.

And the good value is either the previous value or the next value, but you're guaranteed not to get some corrupt value that will make you crash or some half-set value, for instance a rectangle whose origin comes from some place and size comes from some place else.

So that's what atomic guarantee is.

It's not very much.

So it gives you a basic low-level of thread safety for single property but it can actually prevent crashes in the presence of threading in your application.

So it's a good idea.

Now, it's important to note, this does not provide consistency between properties.

It does not give you high level of thread safety.

For instance, let's say you have one part of your program setting the first name and last name on an object, on a person object and some other part of your program reading the first name and last name.

If this guy sets the first name while and then this guy comes in and then fetches the first name and last name, it will get back an invalid name.

That's what I mean by how to look consistency.

You know, it might get back a name like Bill Jobs or Steve Gates, you know, that would be good.

An abomination.

[Laughter] So you want to you know, that's that high-level consistency I'm taking about.

Now, atomicity is still often a good idea to use.

Leave properties atomic.

It is safer.

When you might concern an atomic is, if you notice when you do use instruments for instance, if you know this calls to objc_getProperty or setProperty keep appearing in your back traces and taking some CPU time.

That's when you might want to concern using anatomic.

Another case you might want to drop atomic is if you already have higher level synchronization.

You know, I just mentioned an example of where you might use higher level synchronization.

So if you already have locks or if you're already using cues or if you already have single-threaded access to your objects, you know, as long as those objects are confined to those contexts, you might want to non-atomic properties.

The Cocoa Performance Techniques talk, from two years ago, if you can find the video in our developer's site, has some more about this topic.

OK. So with that, I want to move to the next major section which is Reusability.

And by Reusability, I also mean Extensibility.

In this section, we'll talk about subclassing categories on patterns for communicating changes.

So why is Reusability important?

Well, clearly, there's no need reinvent the wheel over and over.

If you can reuse something that somebody else did, that's good.

And from our point of view, we want to give you API's that allow you to do what every application does, so that you can go ahead write the code on top of it that distinguishes your application.

So we want you to be able to reuse our code as much as possible so you can do the creative things that make your application shine.

And also, you, yourself, can write code which can be reused elsewhere.

Maybe you can write some objects in this application and then use them in another project you're doing by writing reusable code.

So subclassing is the fundamental object-oriented programming feature, of course, for your Reusability.

You know, we all know about it.

But it's not very commonly used in Cocoa and Cocoa Touch for customization.

Most of our classes are concrete, meaning they're usable as is and they're fairly customizable.

So often, rather than using subclassing to customize objects, you just set various properties to make them behave in different ways.

However, we do have some classes which are meant for subclassing.

Sometimes, they're abstract, meaning you have to subclass them.

And other times, they're semi-abstract, meaning their functional as is, but they probably want to be subclassed.

Examples are NSObject, UIView, et cetera, NSDocument.

These are classes that often subclassed.

In these cases you want to identify which methods are there for overwriting.

NSObject init for instance, if you don't have a custom initialization.

If you don't have custom drawing in your UI view, drawRect, and so on.

But then there's this other subtle scenario that we have in our frameworks, and I wanted to talk about that now.

Some foundation classes such as NSString, NSData, et cetera, are absesetract, but they're also fully usable, which means you can alloc init these and you will get back a fully functional instance.

You know, you've done this many times in your code, I'm sure.

However, if you try to subclass these, you find that subclasses do not work unless you've gone ahead and implemented a few methods, overridden a few methods, and these are Primitive what we call Primitive methods.

Primitive methods are the minimal API to implement a new subclass.

In the case of NSString, there is just two methods.

Just by overriding these two methods in your subclass of NSString, you have a fully functional NSString that does everything any NSString can do.

So why do we have this approach?

Why are these classes abstract?

Well, for one reason.

We have a bunch private implementation classes that are hidden behind a facade, like NSString, an abstract class, and we don't want to expose those private classes.

So we have this behavior where subclassing is actually giving you the abstract class.

But another important reason is that we do not want to encourage subclassing these classes for the wrong reason, and the wrong reason being to add additional properties.

And that's because it changes fundamental meaning of this object, what this object stores.

And let me explain that to you.

Here's NSString, it's got two fundamental methods, length and characterAtIndex.

This is how an NSString is defined.

Let's say you subclass NSString with a RichString because you RichString is just like a string.

It's got length, characterAtIndex, but it also has a font.

Now, let's try to use these two classes in a program.

You create a string, its value is hello.

You create a RichString, its value is hello, but it also has the font, Helvetica.

Now you ask the string, is it equal to the RichString?

So the string says, "Hey, my contests are hello.

That other strings contents are hello."

because it only knows how to look at the character values, and it says, "Yeah, sure.

We're equal."

Of course, if you ask the RichString, "Are you equal to this poor string, the RichString puts on a monocle, takes a look and says, "No, that string is not Helvetica.

We're not equal."

So suddenly, you're violating some law, law of physics or math, I don't know, but you have A is equal to B [ Laughter ]

But B is not equal to A, and that's just a very bad thing in programming.

So therefore, this is a bad idea.

Let's take a look at NSString versus NSAttributedSring and how we designed those just to solve this problem.

So NSString has length and characterAtIndex.

We have this other class NSAttributedString, I already mentioned.

It has a string and it has attributesAtIndex to represent the attributes.

So rather than NSAttributedString being a subclass of NSString, it has a string.

And both of these are subclasses of NSObject, and that solves this problem I'm talking about.

So this is a good design here.

OK. So then, the question is, why would you subclass NSString, NSData, et cetera, if you know, we're making it hard to subclass?

Well, there're still reasons to subclass it, and the biggest one is that you want to provide your own implementation.

For instance, you could override the Primitives and have your own storage your data, for instance, goes to a file lazily.

You know, I often get the question, how can you make NSString read a four gigabyte file and efficiently?

Well, you know, you could subclass it to do that, or various other ways of approaching large data storage.

You can have NSString with a tree backing storage if you want.

Just by overriding those two methods, you get that behaviors.

The Class Clusters documentation from the Cocoa Fundamentals Guide goes into more details on this Class Cluster approach.

So we were talking about subclassing so far, and that's one way we can extend our classes.

But another way we extend our classes is via Categories.

Categories is a language feature, and it allows adding methods on existing classes where all instances are affected.

This enables several things.

One of them is that you can actually distribute your methods across multiple header files, which is nice.

But the other more interesting behavior is that you can extend a class without subclassing, so that all instances of that class are affected.

For instance, NSString isn't Foundation.

Foundation knows nothing about drawing, so NSString cannot draw.

However, both UIKit and AppKit add new functionality, add joint functionality to NSString.

For instance, UIKit adds methods, drawInRect withFont, drawAtPoint withFont.

So let me show you how this works.

Let's say we have a drawable string subclass.

So here's NSString, its got length, characterAtIndex, and bunch of other methods.

We go ahead and give you a drawable string subclass which knows how to drink how not to drink.

How to [ Laughter ]

How to draw.

[ Laughter ]

[ Applause ]

OK. And then if you create, if you have other subclasses, some subclass and another subclass, you can see that these subclasses do not know how to draw, so to draw a string you'd have to take these other instances and convert them to drawable strings before they can draw.

So this sort of violates that all impedance matching thing, you know, where you have to convert types just to make them draw.

So, this is not a very good design, but by using categories, you have NSString, the category is added into the string at runtime so some of the NSString knows how to draw.

It replies to drawInRect, drawAtPoint, et cetera.

And then your subclasses are now magically also able to draw although, you know they have nothing to do it because these methods are part of the super implementation.

So that's what categories enable.

Now, in addition to subclassing and categories, one other way to, of course another way to extend behaviors of classes is to have helper classes, helper objects.

And so, we have several patterns for communicating changes.

You're probably familiar with these: delegation, notification, key-value observing, target-action.

If not, we do have some good documentation on this in the Cocoa Design Patterns documentation in the Cocoa Fundamentals Guide.

But let me just go over how these are used to extend behaviors.

Delegation allows an object to act on behalf of another.

It's not a language feature unlike categories.

Classes explicitly support delegates.

UITextView for instance declares that it has a delegate as you can see here with this @property and the delegate responds to a protocol and this protocol lists the methods that the delegate is expected to implement.

In this case, they're all optional so delegate might choose to implement only some of them.

For instance, textViewShouldBeginEditing, et cetera.

Now, delegation allows an object to help another object.

It even allows one object to help many other objects.

For instance, you might have one text view and you might have a delegate.

You might have another text view and it might share the same delegate.

In fact, you might have a table view and it also might share the same delegate.

If you do this, of course, that delegate should respond to the UITableView delegate protocol as well.

As you can see I just added, they are in that bubble.

Now, delegates are also flexible.

There are other object that are also sort of like delegates, for instance UITableView also has a data source and that could be another object, even a distinct object then its delegate.

So delegation allows proper subdivision of responsibility and it's fairly flexible.

You can make it flexible.

So delegation, you know, if I were to give you an analogy, a delegate is like almost your doctor or your lawyer or maybe your spouse.

You know somebody who you trust in and somebody who makes some of the decisions for you or maybe gives you some advice on how to do things, so that's sort of an appropriate analogy for delegation.

You know, for instance, if you have an NSWindow and the NSWindow is being closed, decision whether that window should be closed or not, it's not really the window's place to decide.

It's really the place of whoever has content being displayed in that window to decide whether it's OK to close that.

So that's why the window asks its delegate should the window close.

Notification is another pattern.

Notification allows I was going to use the word "event" here but "event" is so overloaded I said "happenings," which is an interesting word.

It allows happenings to be broadcast to a set of unrelated observers.

These observers observe but they don't interfere.

This is unlike delegation where the delegates do have the ability to interfere.

This also is in a language feature.

There is a class NSNotification center and classes have to explicitly declare notifications they post.

For instance, we have many notifications both in UIKit and AppKit, UIPasteboardChangedNotification, NSWindowWillCloseNotification and so on.

Now, note that sometimes delegate methods, you know they might also be delegate methods that correspond to this and so the delegate might also act as an observer in some cases.

Now notifications is pretty flexible.

You know you can have multiple observers.

There is an indirection between the objects so that observers can actually choose to observe any notification from a particular object or a notification from any object.

That's because, you know, here are the objects we have.

Here are observers.

There is intermediate notification center.

All the notifications are posted there and the notification center sends the notifications to appropriate interested objects.

So, if I were to give an analogy here, this is almost like, you know, somebody whose twittering or I know you're doing it now, but it's almost like twittering or blogging.

Let's say your blogging away telling the world about interesting things that happen to you.

That might turn out nobody is reading your blog or maybe some people signed up for your RSS feeds.

So those are the observers, and you actually don't even know how many observers you have.

You know, they are just independently signing up for your blog and you're just telling the world of what you think is important, so that's like notifications.

You choose to tell the world what's important and they choose to listen if they want to.

Key-value observing is another pattern, I'll just quickly go over this one.

This one allows objects to broadcast updates to individual properties.

You know, like a window can say, my title changed.

An employee can say, my salary changed, and so on.

This also is on a language feature.

Classes decide which properties they want to implement this for.

However, unlike delegation notification, this doesn't require defining any new APIs, any new symbols, and any new protocols.

You know, a class just says, hey, you know, my salary property is observable.

In fact, if those properties are key-value coding compliant, which is pretty much automatic if you use that property for instance, the key-value observing is also automatically available in most cases.

Now, key-value observing is most appropriate when you have UIElements showing various properties.

For instance, you have a text field showing the salary.

That text field is interested in hearing a lot of changes to that salary field and, you know, not necessarily any other notifications coming from that object, so you would use key-value observing for cases like that.

And finally, target action, this is a fairly specialized simple approach.

It allows UIControl syndicate user interaction.

It's simple and it works well in Interface Builder.

You know, control simply sends a customizable action to a customizable target.

This becomes more flexible when you have your target specified as nil because then the responder chain is used to find the appropriate target at runtime.

That's why for instance when you hit command C, an application to copy, the appropriate, whatever the current text field is active, gets to command C.

We don't have to specify that target ahead of time and continuously change it.

The responder chain is a fairly powerful concept that you can also use for your own purposes if you need to.

For instance, we use a responder chain also in error presentation.

We just throw the error down the responder chain in the appropriate window, and if there's no window, maybe the application will be shown there.

The one final thing I'm going to talk about in this section is Model View Controller and this is a huge topic.

In fact, I talked about it earlier.

I just want to have one slide.

Model-View-Controller is a pattern that defines three clear functional roles for objects, the model which owns the data, view which displays and lets the user edit the data, and the controller which coordinates things.

Each piece is separately replaceable, customizable, and that's why this pattern is so powerful.

You can choose to replace the model or the view.

For instance if you're going to write a cross platform app that works well on the iPad, the iPhone, and the Mac, you would use the ModelViewController paradigm to split your pieces so you can reuse your model, for instance.

This is used for overall app design, but also various subsystems through our APIs use this pattern.

The Cocoa text system, the bindings architecture, UITableView, so individual classes even like UITableView, NSTableView, UIViewController use this pattern.

So two talks during the week touched upon this pattern, the model-view controller talk and Advanced Cocoa Text Tips & Tricks talks both showed examples of how we use MVC in our APIs.

OK, so with that, now we're in the convenience section, we're going to talk about convenience APIs and blocks.

So, why is convenience important?

Well, there are three reasons.

One is that it allows you to be more productive.

Convenience APIs allow you to do more with even less code.

It also makes coding fun, you know, effortless.

You know, it seems more easy to do things.

Now, the third reason is that I found out when I was looking at a definition of convenience, I don't know why I did that.

Apparently convenience means public toilet in British English.

[ Laughter ]

And that's important.

[ Laughter ]

And let me use it in a sentence.

At the bash tonight, you will probably need a convenience if you drink too much beer.

Anyway, so with that so, convenience APIs are APIs that simplify or combine a number of other calls into one.

For instance, here's an NSString method that does comparisons and it's got four arguments.

We have a convenience method on it, compare: that just has one argument and basically fills in the other three arguments.

Now, we clearly don't have convenience APIs everywhere we could.

You know, otherwise, sometimes, you look for a convenience API and you don't see it and you have to call a bigger method and you're like, I wish there was a convenience API.

You know if you had convenience APIs everywhere possible, that'd be a lot of APIs, so we usually consider these APIs only in cases where the implementation is more than two lines or there are some additional value in the convenience or there's some valuable abstraction in the convenience, and let me explain what I mean by those last two.

Here are the two methods I showed you earlier.

Here's the way the compare method, the single argument one is implemented.

It simply calls the four argument method with a bunch of default arguments.

I mean, if you look at it, it's really a one line implementation, right?

I mean I graphed it so it looks good, but it's really one line.

You know, you're thinking why is there a convenience method here?

Well, that's because there is additional value in this method.

The compare method allows you allows you know it can be used in sorting methods.

For instance, NSArray has sort using selector.

It takes a selector with one argument.

So, you can just pass the compare method right in there.

In fact, we have more NSString comparison conveniences such as case insensitive compare, localized compare and so on just for this purpose.

So the there's additional value in these methods, in these conveniences.

Another reason is valuable abstraction.

Here's a method we recently added in Snow Leopard and iOS 4.

This does a comparison just like the system does and we described that this method's behavior may change over time.

Here's the implementation we tell you.

This is if you look in the release nodes, this is the exact implementation today.

However, as I said, we may change this implementation over time.

So here, this method is valuable because of its abstraction that the implementation may change, so it's more than in fact the convenience in this case.

By using it, you will get this abstraction of comparing just like the system does.

OK, so the next topic here is blocks.

Now, when you think about it, blocks are mostly for convenience.

Blocks do not really enable anything that was impossible before.

You know, they bring a lot of convenience.

You can specify a piece of code in line.

You don't have to create an extra function, and they have this ability to capture state, which you can also emulate.

Now, I don't want to sell blocks short.

Blocks are an amazing feature.

In fact they are so amazing we added them to the language.

In fact, we added them to C, you know, for which the bar is pretty high, so they are an amazing feature.

But in the end, they really bring a lot of convenience.

For instance, we use blocks as callbacks and we're replacing the places where they appear as callbacks.

In fact, all those compare variants are really not that necessary anymore.

For instance before, if you did not have a compare method you might have wanted like a numeric search, you would have had to go and define your own custom function and then pass it to myArray, you know, NSArray sort using function.

While before with blocks, you can just do sortUsingComparator and pass the block right in there and the code you want to do is right in there, so this is pretty convenient.

You don't need all those compare variants anymore.

Another thing we use blocks for is completions, for instance new APIs for presenting sheets in AppKit and in the same panel.

Here's what the Leopard API looks like and here's what the Snow Leopard API looks like.

It's considerably simpler.

Note that we replace these last three arguments to delegate the selector and the context in full with just the block.

Also note that one thing we've done here is get rid of this pesky void star argument.

Refining that, void star arguments are really more trouble than they're worth and we're looking at getting rid of them from our APIs as much as possible, and for one thing they really do not behave very well under GC for instance.

Here, one final example of blocks as conveniences, the new APIs we've added in UIView, for instance animateWithDuration animation looks fairly convenient.

In fact, it replaces three lines of boilerplate code like here, that this is the code you would have had to use on iPhone OS 3 with just one line where you're specifying the animations you want right there in the body of the block, so again, super convenient.

Hopefully, here you've heard about how good API is very important part of Cocoa and Cocoa Touch design.

We take a good API design very seriously and I hope I've been able to communicate how we do that and how you can also do it yourself in your own applications and in your own designs.

Now modeling your APIs as ours will allow your APIs to be more predictable so you can actually know almost all the new method names, just roll off your tongue, you don't have to worry about them.

Widely reusable, you can use it in other projects, give it to other people, and they will also be better performing as you hopefully saw.

And one final warning, you know, API design is an evolving art.

We have examples of some APIs that are not perfectly named.

So, if you ever run across something that looks a bit fishy, maybe it is.

So don't take, you know, everything strictly, but again most of our APIs are going in the right direction.

For more information, you know, you can contact our evangelist and we have lot of great documentation, just the starting point, developer.apple.com.

You can do searches for the various keywords I gave you, and there is a lot of great background stuff on this.

Apple, Inc. AAPL
1 Infinite Loop Cupertino CA 95014 US