iPhone • 1:09:34
Innovations are a core element of Cocoa development on Mac OS X, from new compiler technologies, runtime advancements, and multi-processing APIs to high-level frameworks for application design, interaction, and presentation. Add your own creativity, and the result is a truly modern application. Discover how all of the core Cocoa technologies come together to help you create state-of-the-art products.
Speakers: Jon Hess, Mike Ferris
Unlisted on Apple Developer site
Downloads from Apple
Transcript
This transcript has potential transcription errors. We are working on an improved version.
Good morning. My name is John Hess and today I'm going to be joined by my colleague Mike Ferris. And the two of us are going to tell you about state of the art Cocoa application development on Mac OS X. Mac OS X has a whole host of technologies that are available for you to use in your applications.
And it's our aim today to take you on a tour of each of these technologies so that you can see what's applicable to your application and help most effectively plan your session attendance for the remainder of the week. I'm going to start today by telling you about some code clarifying technologies.
These are technologies that you can use to help your application focus on what it does best. You can eliminate the code that's common to all sorts of applications and really just focus on the features that you deliver to your users. First, I'd like to talk about garbage collection and Objective-C properties.
These are two technologies that you can use to help manage object life cycles in your application. Now, if you've written a manually memory managed application on Mac OS X before, there are a couple of problems that you need to, that you've been aware of or potentially had to debug. For example, if you over release an object or free it too aggressively, you've probably run into a problem with your application crashing, perhaps due to memory corruption. These bugs are typically very difficult to diagnose and correct.
Additionally you may have forgot to release an object or to free it. Doing this typically results in a memory leak in your application, which could range anywhere from being a small performance problem to a major show stopping bug. Additionally you've had to worry about the problem of retain cycles. A retain cycle is where you have one object that has a strong reference to a second object and that second object has a strong reference back to the first.
This is a specialized form of leak and these two objects retaining each other causes them both to stay alive. Now, you may have even become good at diagnosing these problems and fixing them, but you're having to devote mental capacity in order to keep your application functioning properly and to not commit these sort of problems in your application.
Garbage collection that comes to us on Mac OS X, 10.5 Leopard with Objective-C helps you just eliminate worrying about these type of problems in your application. Garbage collection can also give you a bit of concurrency for free in your application. If you have one thread in your application that's busy computing results for some important user task, the objects that you're generating while doing this computation can be collected in the background by the garbage collector. This gives you some immediate concurrency. There are a couple of things you need to know when migrating to garbage collection. One of them is that there are many APIs that you might interact with that aren't themselves dealing with garbage detective memory.
For example, if you're using core foundation types in your application, you'll still need to call CF release and CF retain on them manually. Optionally you can opt those CF type objects into garbage collection with the CF make collectible call. A second technique that's available to us through Objective-C 2.0 are properties. Properties are all about simplifying the way that we declare, define and use the attributes of our object. The best way to talk about properties is with an illustration. Here we have a hypothetical My Person class and we're looking at the header file for that class.
In that header file, we have a number of accessors declared to control things such as the first name, the last name, and the full name. If we were to flip to the implementation file for this source file, we'd see code similar to this. This code isn't interesting, it's kind of long, and it's exactly the kind of code that you're going to gloss right over during code review. This makes it susceptible to problems like copy and paste bugs where you have one property that's inadvertently controlling the wrong instance variable.
Objective-C properties help us eliminate these problems in our source code. So here's an example of the same class implemented with Objective-C properties. The first thing you'll probably notice is that in the interface for this class, we've used this new Ap Property compiler directive. The Ap Property compiler directive tells the compiler this class is going to have an accessor pair for in this case a first name property that's of type string.
And that copy indicates that when setting the string for the first name, we're going to use copy semantics. We've done the same thing for last name and then declared an additional read only property full name. That full name property is declared read only because it's actually a derived property that we're going to base off of the first name and last name.
If we flip to the implementation file, the first thing you'll probably notice is that it fits all here on one page. It's very short. All the redundancy of getters and setters is eliminated. We're able to use the At Synthesize compiler directive to ask the compiler to automatically generate getters and setters for our first name and last name properties that correspond to the properties in our interface file. We're also able to go ahead and implement the full name accessor our self.
This allows us to have full control over the implementation of properties if we'd like them. And it's a great place for us to add custom logic for properties that are based on the values of other properties. In addition to helping you declare and implement accessors for your attributes, properties also give you, give clients a new way to access state of objects.
And that's with the dot syntax. The dot syntax is syntactic sugar that still translates to the familiar getter and setter messages at runtime. But by using the dot syntax, you can be more clear in your code. It's clear when you see person.firstname = Bill that you're just configuring the person object.
Another benefit of using the dot syntax is better code completion. If you were to type [person codecomplete] in X Code, you'd get a list of all selector messages which are valid to send to this person. That's going to include lots of messages that are valid for every NS object. With properties, you get exactly the set of properties that are available to this object, which gives you a much more precise code completion list.
So properties help you clarify your API and give you a nice concise expression. In your header file, it's really clear what method you use to configure the state of an object when you use the At Property compiler directive. And it lets you clearly differentiate between state and behavior because if you adopt properties everywhere, all of the property methods are used to configure state and all of the other messages are used to make the object do things that control its behavior.
If you'd like to learn more about garbage collection in Objective-C properties, tomorrow at 5 PM in Russian Hill the Objective-C and garbage collection advancement session will take place. The next set of technology that I'd like to look at are things that you can use to help eliminate model view controller glue code in your applications. The first of these is key-value observing. Key-value observing is all about eliminating the direct coupling of your controllers from your models.
In a model view controller application, it's the model's job to notify the controller when changes take place. And that typically means that you have to have a manual notification code in your models. Now where that code is and how it gets posted is typically really driven by how your controllers want to interpret those messages.
And so you really do have sort of the design of the controllers driving the design of the model. You can help break that down and have two more independent set of classes by using key-value observation. Key-value observation is a way for a controller object or really any object to register that it's interested in change notifications for a second object.
So a key in key-value coding is just a short string like first name, or last name, or full name if we were to consider our people example earlier. And a controller object that's managing people can register for change notifications whenever a person object changes. Now all a person object needs to do to cooperate with key-value observation and be observable is really just use its own setters in order to mutate its state.
Key-value observation is really a foundational technology. Lots of things are built on top of it. An example of something that's built on top of key-value observation is Cocoa Bindings. Cocoa Bindings really let you eliminate the whole swaths of model view controller glue code. So for example, in a typical model view controller application, you might have a window with a text field on it and when the text field has a value entered in it by the user, the text field notifies a controller. And the controller reacts to that value being entered and decides to modify a model object.
The model object might have cascading changes that are caused by this change and after the changes, while all the changes occur the model notifies the controller. Which in turn captures those notifications of changes to reupdate the view. This is exactly the kind of code that Cocoa Bindings eliminate for you. They help do that data passing all the way from your view to your model and back again without you having to write code to shuttle it at each stage.
So the way you use Cocoa Bindings are you can go into for example Interface Builder and select some element that's in your user interface and you can tell that element that it is bound to a specific attribute. For example first name on a controller where that controller manages a list of people. And now you'll have a text field that's able to control the first name attribute of people. Cocoa Bindings make it really easy to deal with things like multiple selection or single selection of course or not applicable selection or an empty selection.
They really make it as easy as handling the standard single selection case. So using Cocoa Bindings is a great way to eliminate model view controller code from your application while still keeping a model view controller design. You can learn more about Cocoa Bindings in Exploring Cocoa Bindings tutorial lab. That's today at 330 PM in Mac OS X lab B. Next I'd like to look at some refactoring opportunities that are available to you with new classes in the AppKit and new language features.
The first of these are view controllers. View controllers are all about dividing up the discrete areas of responsibility inside of multi -component windows. So here's an example of a common interface lay out that we'll see in applications on Mac OS X. I like to call it the master master detail interface. So in this application, we might have a source list where a selection in the source list drives what appears in the content area, which is probably another list.
And selection in the content area drives what appears in the detail area. An application that uses this pattern might be mail where you have your mailboxes in the source section, your messages in the content section, and the content of each message in the detail section. RSS readers and X Code are another example of applications, which typically follow this pattern.
So view controllers are available to you in Mac OS 10.5, Leopard through the application kit and are really a formalized way for you to break down this sort of application in your source code. Before view controllers, you might have implemented such a window with just one monolithic window controller where the window controller is responsible for managing the source list, the content list, and the detail area.
Or you might have written your own set of classes to break this functionality down into its different discrete components. But that would've been something you rolled yourself. You might not have followed that pattern consistently. By using view controllers in your application, you can use the same sort of pattern everywhere and really cleanly factor your source code.
That'll help make it more malleable in the future should you decide to change the way your application works. Another exciting technology that's available on Snow Leopard are blocks. Blocks are really changing the way we do all sorts of things. Blocks are all about making callbacks dead simple. One of the complicated parts of doing callbacks before blocks is context management. Typically the callback function that you'd like to have invoked by some service needs some data to be passed to it. Blocks eliminate out of band data passing between context functions and the person that invoked the operation that was going to involve a callback.
I'd like to illustrate blocks with an example. We have the people object we talked about earlier and this is going to be a method that sorts those people objects by a particular key. Where key is something like the first name or the last name. It's the same kind of key that we would use in key-value observation.
And we want to do a stable sort. And if you're not familiar with a stable sort, a stable sort doesn't mean that it's not a buggy sort. It means a sort that's going to sort in a specific order. For example, if you have two objects in the input array that would be tied in terms of the comparison function, they'll appear in the output array in the order they appeared in the original array.
So for example, if our original array had two people Matt Gamble and Matt Furlick appearing in this order, and they were sorted based on the first name, there would be a tie because they're both named Matt. With a stable sort, Matt Gamble who appeared first in the original array would appear first in the output array. Many sort routines are not stable. And so they don't give you the same variant. But you can always build a stable sort on top of an unstable sort by taking the original order of your objects into account in your comparison function.
That's what I'm going to do in this example. So to do this we're going to build a map table. The map table is going to hold an entry for every person that we're sorting. We're going to populate that by iterating all the people in order and putting an integer into the map table that's the index at which that person appeared in the people array. Then we're going to ask the array to sort itself. And to sort the array, we're going to pass in a my compare people function. Now that's a function pointer.
And our my compare people function needs to know how we're sorting. For example by first name or by last name. And it needs some, and it also needs to know the original order of the object so that it can do a stable sort. To do this, we use this context parameter that we're needing to pass out of line. Now we'll scroll up. You'll see some other code that I have to write in order to make this happen.
I've got to declare a structure to hold this context. These are the arguments that I want to pass out of line to my compare people function. And additionally I've got my function. Now this is the meat of my whole routine. This is what's actually doing the sorting. It's the most interesting part. But it's not, it's not in there with the rest of my code.
I'm having to spread everything out across an entire file. Let's look at how we do this with blocks. Here I'm going to use exactly the same techniques, but it's going to take a lot less code and I'm going to get to write my code in the order I think about performing the operations. So I'll declare the order table just as before, but then I'm going to use a new method on NS array called enumerate objects using block.
This method takes a block and the block takes three parameters. The object that's being countered during the enumeration, the index that that object occurred at, and an out parameter that I can use to terminate iteration early. We're going to visit all the objects and build up our table just like before. Then we're going to ask the people array for a sorted version of itself. And we're going to use the new NS array method from Snow Leopard, sorted array using comparator.
This method also takes a block and that block takes two arguments. A person A and a person B and it's our job to declare through the return value of this block what the order is that those two people should appear in the output array. We're going to sort them exactly as we did before.
You'll notice here that it didn't have to have any structure to declare the arguments that I wanted to pass into my callback function because in this case the callback function is a block. And the block is able to close over the lexical scope of where it's created and access all those variables. So here in my block callback I'm able to just access our order table and our key argument without having to have any out of line data. This really helps me write the code in the order that I think about it.
It just flows much more naturally. It also helps me keep my file name space tidy. I don't have to have these pieces of functionality that are exactly interesting to one function living at file name space and sort of being available for example in the X Code, function pop up or just you know being potentially interesting to people when really they're particular to my one function. So you can learn more about blocks in Programming with Blocks in Grand Central Dispatch. That's tomorrow at 2 PM in the Marina. That concludes the code clarifying technologies that I wanted to take you through today. Now I'd like to look at some performance opportunities.
Everything is 64-bit. You should absolutely be making your application 64-bit for Mac OS X Snow Leopard. One of the main features that you're going to get by migrating to 64-bit are there are twice as many registers available in X8664 architecture versus the previous I386X architecture. This is going to mean that the code that your application, that the compiler generates for your application is going to be able to run faster. When we added Objective-C 2.0 to Leopard, there were some features that we couldn't add without breaking binary compatibility.
And we never want to break binary compatibility. So in order to add these features, we only added them to the new 64-bit runtime. While not having a history, we could, we could change things there and give you some new features. An example of some of those are no fragile base class problem.
So you can redeclare instance variables in a framework class that's sub classed in other binaries without having to recompile the other classes. You also get instance variable synthesis, which means that you don't have to declare instance variables to match each of the properties that you're using with ApProperty and ApSynthesize. And finally you get zero cost unified exceptions. This means that the same exception mechanism is used for C++ and Objective-C. And it means that entering a try block is cheap. Before entering a try block was expensive and now it's really inexpensive.
All apps should be 64-bit on Sdnow Leopard. We're increasingly shipping more and more of our content as 64-bit. We have, today I'm telling you all about frameworks and technologies and all these frameworks and technologies you get to leverage really cheaply in your application because we've written lots of code for you. All of that code that we've written is compiled two ways.
It's compiled for 32-bit and it's compiled for 64-bit. And when you run your application as either a 64-bit process odr a 32-bit process, you automatically use the right set of this code. Now at the point where we get to having no 32-bit processes, none of that 32-bit framework stack will need to be loaded.
And if you're the last application on the user's machine that's running 32-bit, it's going to mean that your application is going to fault in all of those frameworks. And that's going to be expensive both in terms of I/O time and memory usage because all those 32-bit frameworks aren't going to be amortized across multiple processes. They'll only be used by yours.
So don't be the last 32-bit application on a user's machine. Finally, you should absolutely use the 64-bit transition guide when transitioning your application to 64-bit. It's a guide that points out lots of things that you need to be aware of when transitioning to 64-bit and it adlso comes with a helpful conversion script that does much of brunt work of translation for you. The next technology that I'd like to discuss with you today is Grand Central Dispatch. Concurrency is a unifying theme in Snow Leopard.
As our machines have more and more CPU cores, we have to leverage parallelism to make our applications faster. Now, I like to start thinking about Grand Central Dispatch by thinking of a technique called thread pooling. If you have an application with lots of units of work that you'd like to do, for example an application like X Code you might want to index dozens, hundreds, or thousands of source files.
You could do this by just creating one thread for each source file you wanted to index. But if you had 1000 source files, that would be very expensive. It would be expensive because scheduling threads takes time and threads all require data in order to operate. They all have large stack space, hundreds of kilobytes.
So you don't want to create a huge number of threads proportional to your number of jobs. Instead you want to create a number of threads proportional to the execution resources that are actually available on your machine and then have your jobs run on those threads. A problem with this technique in many times is that lots of libraries implement this internally. And then rather than each library creating just the right number of threads, you wind up with N times as many threads as you should have given that where N is the number of libraries that implement this technique.
Grand Central Dispatch comes in and implements this technique at a low level in the system so that everyone can be built on top of it and we can get better system utilization by creating just the right number of threads on each machine. So Grand Central Dispatch works in terms of blocks.
It uses blocks as the currency of concurrency. Each block is one unit of work or one task that you might want to do. And with Grand Central Dispatch, you schedule those blocks on queues. You can schedule them on two kinds of queues. The first kind a serial queue. You schedule blocks on a serial queue and they'll run in the order they were added to the queue.
But you have lots of queues that are serial queues and each queue runs independently and concurrently with respect to the rest, but serially internally. You also have concurrent queues. A concurrent queue is a place where you can place a block and just have it execute concurrently with respect to all of your other queues and blocks. So Grand Central Dispatch gives you new synchronization concepts.
Rather than perhaps having a mutual exclusion law that you use to protect shared data and you run code for multiple threads and use the block for exclusion, you can actually run all of your code that accesses the shared data on the same thread by guarding it with one Grand Central Dispatch queue and placing blocks that access that data on the queue. So because of blocks, this type of callback mechanism to do concurrency becomes really easy.
You get to capture the state you want and execute it on the right time on the right thread without having to write a bunch of P Thread code to put that all together. Besides just raw concurrency, Grand Central Dispatch also gives you event monitoring. You can use Grand Central Dispatch to for example ask for a notification.
And the way you'll get a notification is by providing a block and a queue that you'd like to be executed on. And an example of such a notification would be to have your block executed whenever file I/O becomes available, perhaps on a socket that you're reading. You can also monitor files moving on disks, processes dying and UNIX signals.
Built on top of Grand Central Dispatch in Snow Leopard is NSOperation and NSOperationQueue. Now NSOperation and NSOperationQueue also predate Grand Central Dispatch and exist on Leopard. These are the Cocoa abstractions to these parallel programming techniques. By programming at this higher level, you can be abstracted from how system changes happen underneath you. Additionally you can target Leopard where NSOperation and NSOperationQueue are also fully supported. One feature that these classes offer above what you get with Grand Central Dispatch is dependency management.
You can declare that some operation, which is a unit of work just like a block in Grand Central Dispatch, is dependent on some other operations. And you can queue them all together and the operation system will figure out how to execute your operations in the right order so that all the dependencies are satisfied. So I told you that concurrency was an important theme for all of Snow Leopard and everywhere today. We're backing that up with all these sessions.
You can get started learning about concurrency in your Cocoa applications in Designing your Cocoa Application for Concurrency. And that's going to take place in this same room today at 330 PM. So Grand Central Dispatch and NSOperation I told you about how to take advantage of your CPU and its multiple cores. But if you really want to get maximum parallelism in your application, you need to take advantage of all of the execution cores that are available on your machine.
Your machine also has a GPU and it has dozens and dozens of cores that are able to execute code extremely quickly. Typically taking advantage of your GPU for general purpose programming tasks has been difficult. You've had to write vendor specific code and only operate on machines that have hardware that matches your vendor specific code that you used. With OpenCL you get to program in a familiar C99-like language that's cross-platform and an open standard.
By targeting the GPU you can get huge performance increases by having lots of parallelism. And these GPUs are great at executing a specific kind of task. And that's a data parallel task. What I mean by data parallel, is that you have the same block of code that you want to run over and over again on different segments of data. Your GPU is great at executing that. Another benefit of targeting OpenCL is OpenCL uses dynamic compilation.
That means that when you write your program in source form and when it's time to run it, OpenCL compiles that program down to the best instructions that run on the GPU that's in your machine. Additionally OpenCL can target the CPU and the CPU might run a different set of instructions, well it's obviously going to run a different set of instructions, than your GPU. But where this gets interesting is when your application is deployed on a machine that doesn't have a fully programmable GPU that can run your OpenCL program.
OpenCL lets you run that program on the CPU. Thursday is the day for OpenCL On Thursday in the Marina all day you can learn about OpenCL. At 9 AM, 1030 AM and 200 PM and then again in the Graphics and Media Lab B at 330 PM are OpenCL sessions.
Now I'd like to talk to you about some media technologies that are in Mac OS X that you can use to really just dazzle your users. The first of these is QT Kit. QT Kit is the Cocoa interface to QuickTime and QuickTime is the media framework for Mac OS X.
With QT Kit, you can leverage QuickTime to do media playback, editing, capture, import, and export. And what's exciting about QT Kit is that in Snow Leopard, it's built on top of QuickTime 10. QuickTime 10 is a lightweight media playback framework with improved thread safety. It's fully 64-bit and it supports modern codecs. You can learn more about QT Kit and QuickTime in Transitioning to QuickTime 10, which takes place in the Marina today at 200 PM. Next is OpenGL. I told you all about how you can leverage your GPU with OpenCL to do concurrent programming.
You can also use it for its original task, which is great 2D and 3D graphics. So on Mac OS X, OpenGL is the primary interface for the GPU. And lots of the other media technologies that I'm talking about today are built on top of OpenGL. It's a really exciting time for OpenGL right now. OpenGL 3.0 is improving and expanding rapidly. OpenGL is available on all of our platforms so you can do OpenGL programming on the iPhone. We saw yesterday that OpenGL ES 2.0 is now available on the iPhone 3GS.
So it's a really great time for OpenGL with lots of movement. OpenGL Techniques for Snow Leopard is taking place tomorrow in the Marina at 1030 AM where you can learn more about OpenGL. Next is Core Animation. Core Animation is one of my favorite of the media technologies. Core Animation sits at a high level and gives you a familiar object abstraction for manipulating what they, what Core Animation refers to as layers. And layers are 2D pieces of co-ntent that live in a 3D space. With Core Animation you get to define implicit and explicit animations.
Explicit animations are the type of animations you might expect where you say, take this object and modify these properties and make it take place over this amount of time. Implicit animations are much more interesting. With an implicit animation, you can go to some object and configure it such that for example when the X parameter of this object changes, it should be animated in this way. Then later in your program you simply change the X parameter of your objects and they animate in the way you specified earlier. Implicit animations make doing animation with Core Animation great. Core Animation also integrates with all of our other media technologies.
So you can have custom OpenGL content in your Core Animation layers, you can have custom QuickTime content and Quartz Composer compositions. Finally Core Animation integrates directly with Cocoa and Cocoa Touch. So you can host a Core Animation layer hierarchy right in an NS view or UI view. Core Animation Techniques for iPhone and the Mac is taking place tomorrow in the Mission at 900 AM.
You can go there to learn all about Core Animation. The next application that I'd like to talk to you about in media technology is Quartz Composer. Quartz Composer ships with the development tools and it's like a graphics IDE that you can use on Mac OS X. You can use it for defining data driven transitions and effects. When you launch Quartz Composer, what you work with is a network of patches. Each patch is a computation unit that has inputs and outputs.
And you bring lots of patches together into one document and wire their inputs and outputs to each other in order to form a data flow that describes a transition or animation. Quartz Composer comes with a library filled with just dozens and dozens and dozens of patches. One of the patches is an OpenCL patch where you can write a custom, where you can write custom OpenCL code for your Quartz Composer compositions. When you're working with Quartz Composer, one of the great features is that you get live previews of what you're doing all the time.
So while you're in here working in a patch editor, wiring your patches together, you'll have a window on the screen that's showing you exactly how your changes to the patch configuration are going to affect the transition that you're building. You can use the Quartz Composer composition that you build directly in a Quartz Composer view that's available to Cocoa applications through Interface Builder.
You can also host your Quartz Composer content directly in a Core Animation application via CLAR. Quartz Composer Advances in Snow Leopard is taking place tomorrow in the Marina at 500 PM. There's also a follow on lab on Thursday at 330 PM in the Graphics and Media Lab A.
Multi-Touch is really changing the way that we think about user interfaces. Ever since the iPhone brought Multi-Touch to the foreground it's just been a great way for the users to interact with your applications. And on Mac OS X Snow Leopard the Application Kit is bringing Multi-Touch and gesture support to your applications.
Now, Multi-Touch is the idea that you get to put two fingers down on the track pad or maybe three fingers or any amount really and do some gesture with them and track all of those fingers moving in space independently. Now on top of Multi-Touch, you get to build things like gestures.
Where a gesture is an abstraction of Multi-Touch that is something like putting two fingers down and twisting, means to rotate. Now rotate is a much more direct way by doing a gesture like that for the user to interact with your application. I find putting two fingers down and rotating much more intuitive than selecting a text field in an inspector and inputting an angle value. So with the application kit on Snow Leopard, you can leverage Multi-Touch directly through NS event and you can leverage gestures through NS responder.
Gestures are actually available on Mac OS 10.5 and later. And all of our current laptops have Multi-Touch capable track pads. So there's no reason not for you to start adopting Multi-Touch in your applications today. In this same room tomorrow at 330 PM, you can learn about User Events in Cocoa which will include information about gestures and Multi-Touch.
The last technology that I'd like to talk to you about today is Core Location. Core Location is all about building location based services. We saw in the keynote yesterday an example of a Zip Car application that allowed the user to find available cars that were near where they were at. These kind of applications are great. They make using them just a breeze.
You don't have to do awkward things like enter zip codes to find out what information is relevant to where you're at. I'm always impressed by the different Core Location and location based service applications that I see on the iPhone and I would like all of you to consider adding location based services to your applications and impressing me on Mac OS X.
You can do that now in the same way that you do it on the iPhone. It's really simple. You just link the Core Location framework into your application, create an incidence of the CL Location manager class, and attach a delegate to it. And that delegate will get live notifications of changes that occur in the user's location. And you use that to build location based services.
So I'm really looking forward to how all of you can impress me with location based services in your applications. You can learn a little bit more about Core Location tomorrow in the Presidio at 200 PM and with that I'd like to turn it over to my colleague Mike Ferris to talk to you about how you can handle data on Mac OS X.
Hi. I'm Mike Ferris and I work on XCode but today I'm actually really happy to be able to be here to talk to you about some cool Mac OS X technologies that you can use in your applications to make them really great.
Now John talked a bit about some of the Objective-C language features that are really useful to make great applications as well as about some important new foundational technologies and finally about some of the presentation technologies to help you to make your interface really great. I'm going to concentrate on the data that your application produces, what the user authors in your application.
Almost all applications let the user author data. Data of lots of different types and so forth. And mostly these applications break down into one of two categories. They can be document-based applications or they can be what I like to call Shoebox applications. In a document-based application, the user is thinking about creating documents They create separate documents for different purposes, these documents aren't necessarily related to each other, and the fact that the user is thinking about the documents is key to a document-based application. You can see examples of some document-based applications from the icons up on the screen.
A Shoebox application on the other hand, the user thinks about working with their data altogether in your application. Your application is actually the user's focus of attention. And all of the data is in there being organized within the application. They're not thinking about authoring separate documents. Examples of Shoebox applications include things like iPhoto, Mail, Address Book. People don't think about where they're saving these things.
They just know that all their stuff is in there. Now regardless of which of these types of applications you're creating, there's a couple of decisions that you should really make up front. You want to decide whether to use NSDocument and then you should decide whether or not Core Data is an interesting technology for you. Now these are actually independent questions that are not mutually exclusive and let's tackle them one at a time. Whether to use NSDocument, well that's an easy one. If you're building a document-based application, you should use NSDocument.
If you do, you'll get a lot of advantages. You'll write a lot less code for handling standard document functionality. NSDocument deals with running Open and Save panels, with tracking the dirty state of your document, making sure that's coordinated with per document Undo managers. And with associating your documents with the windows that contain the interfaces that present them. Because of this, your document-based app will get consistent behavior with all the other document-based apps on the system. They'll be more familiar to the user and the user will know what to expect when it comes to how to deal with documents.
It's also worth considering that a lot of these standard behaviors have complex subtleties that are not necessarily really simple to get right if you're doing it yourself. So for example, NSDocument when it goes to save things it makes sure to handle replacement warning dialogs, it saves things atomically to provide better data safety.
It makes sure that the Recent Documents menu tracks properly with changes that you're making to where documents are stored. And it has really good error handling and presentation built right in. You don't have to worry about that stuff. Now, also NSDocument has integration with a number of other important Cocoa features.
NSDocument works directly with the auto saving functionality in Cocoa as well as with the new sudden termination APIs to make sure that sudden termination gets turned off when you have unsaved changes in any of the documents in your application. And then finally, if you're also using Core Data, NSDocument integrates well with Core Data through the NS persistent documents subclass. You can go to either of the two Cocoa Open Labs to learn more about NSDocument and ask any questions that you might have.
And for documentation, you should start with the Document-based Application Overview and the Application Architecture Overview. The second question is whether to use Core Data. This one's actually a little bit more complicated. It really depends what kind of data your application works with. Core Data is really good at managing interconnected graphs of objects. And if you have data that looks like that, then Core Data may be a great fit for your app. There are a couple of caveats.
First to use Core Data, you really have to be able to define a fixed schema for your data. If the structure of your data is too malleable or if the user gets to change it and modify it on the fly, the structure of your data, then Core Data may not be the best fit. Also Core Data isn't a client-server database solution.
So if you need to deal with database servers, Core Data doesn't help you to solve that problem. Now it may be that Core Data is a really great fit for some of the data that your application works with but not for other data. That's fine, you can use Core Data for whatever makes sense and then handle the rest of it yourself.
If you can use Core Data, it brings a lot of advantages. Core Data has really great object graph management. In fact, I'm going to talk about that a little bit more in a moment. It also has automatic support for persistence, for data persistence in a number of different file formats including SQL Light and XML. And if you're using SQL Light, you actually also get incremental loading and saving which can be a huge win if you're dealing with a lot of data.
Core Data is directly integrated, in fact it's written to work well with other technologies like bindings and the Cocoa Controller objects, which makes it that much easier for you to get your data into the user's interface, into the user interface where the user can actually see it and edit it.
As we heard yesterday or possibly before, Core Data is now also available in iPhone OS 3.0. So that gives you an opportunity if you're using Core Data to share code between your desktop applications and your iPhone applications at the model layer. And finally Core Data has direct integration with a number of other Mac OS X technologies that I'll be talking about in a moment such as Sync Services and Spotlight. Before we wrap up with Core Data here, I wanted to talk a little bit more about this object graph management business. Everything you do with Core Data takes place within a managed object context. And this managed object context provides a lot of functionality for you automatically.
It has Undo support, so any changes that's being made to your model objects automatically support Undo. It has well defined hooks for validation and for conflict resolution. It provides KVO notification as well as Course or Grain Change notification automatically. And it has bidirectional relationship management. So if you have you know some of your objects point to another kind of object and then those objects point back to the ones that pointed them, both ends of that relationship are managed together to maintain integrity of your object model. And finally it supports delete rules that allow you to properly propagate delete behavior across relationships.
We've really, we've just scratched the surface on Core Data. And to learn more you can go to the session this week as well as to the Core Data Lab. For documentation you should start with the Core Data Overview or the Core Data Programming Guide. OK before I move on to some of the cool system integration technologies, I wanted to take a moment to talk about where your application and the data that it creates should fit into the user's system. We'll talk about where your application goes, where the data that the user authors in your application goes and where anything else that your application might need goes.
Your application should be self contained and relocatable. Ideally it should just be a single app bundle that the user can put wherever you want, wherever they want. If that's not possible, if you have a more complex application, maybe it's a suite of applications that need to share frameworks or something, the next best thing would be to have a folder that contains all of those things and make the folder self contained and relocatable. This gives the user the greatest flexibility in terms of where they want to install your application.
For the data that the user authors within your application, well in a document-based application, the user is thinking about that data as documents and they should be the one who decides where it goes. Your Save panels can default to the user's documents directory but ultimately it's up to them where the stuff gets put. But that's not quite the case for Shoebox applications. In a Shoebox application, the user really isn't thinking about where their data goes. It's in the app.
So you need to decide as the app developer where this data gets stored. And generally it should go into the user's library. And what you want to do is create a folder in there that's named for your application identifier so that it won't collide with any other application.
And if you want to be nice about it, you should put a display name on that folder that's your application's name in case the user goes browsing around in the library, they won't be so confused. Finally there's other stuff. Sort of optional data that may be accumulated along the way as the user uses your application.
This is the sort of thing that you can put into the user's application support folder. Application preferences should just be managed through NSUserDefaults, let it worry about where to put it. If you're generating cache data, there's a special folder for that, the user's Caches folder. And then finally if you have the need to create temporary data, you should use a temp folder for that. Now I've talked about a bunch of locations and sort of mentioned them by name.
There are APIs on the system that will help you to find out where these specific places are. The main one is NSSearchPathforDirectoriesandDomain. This API you basically give it a constant that identifies a special place and it'll return to you the path where you should put the path for that folder.
So the application folder or the document folder, et cetera. For temporary directories, there's actually a specific API and its temporary directory can be used to find a secure location to store temporary files. OK, enough about disk drives. Just to review for a second. We've decided whether we're using NSDocument. We've decided whether we're using Core Data. Now let's talk about how your application can make sure that the data that the user authors with it fits really well into the user's overall environment.
The user creates things with your application and it may be the primary place where they work with the things that they create in your application. But that's not all they do. Sometimes when they're not in your application, they may want to find things. They want to preview things. They want to show things to other people. Maybe they want to synchronize data between multiple different devices. Your application can make it possible for them to do that with the data from your application as well.
Before we get into the individual technology though, a lot of these things are predicated on understanding how Mac OS X deals with file types. And this is actually a key concept that you should be sure to familiarize yourself with. Data comes in lots of different flavors or types. And on Mac OS X we use UTI, uniform type identifiers to specify what the type of a file is. UTIs can also be used to map back and forth between MIME types or path extensions, but it's the UTI that's the primary means of identification for file types.
They're used all over the system. Launch services uses these to associate documents with the applications that can open them. NSDocument is based on UTIs, NApps -- sorry, technologies such as Spotlight and Quick Look are also based on UTIs. So it's really important to understand how these work. To learn more about UTIs, you should check out the Uniform Type Identifiers Overview.
Alright, let's get into some of these cool technologies. The first one I want to talk about is Spotlight. Spotlight is about system-wide searching. It allows the users to find anything throughout their system. And it works on an index that's built up of metadata about each file on the system. Plug-ins do the indexing.
So when it comes to needing to understand the metadata about a particular type of file, Spotlight looks for a plug-in that can do that. Now your application can provide Spotlighter, Spotlight importer plug-ins for any custom data types that your application defines. And it can also make use of Spotlight internally.
The first thing that a Spotlight importer plug-in does is it declares the UTIs that it knows how to index. This is again how Spotlight finds the appropriate plug-in to use for any given file. They also need to define a metadata schema that basically declares the different kinds of metadata that the importer will be able to find about the files that it indexes. Apple defines a whole bunch of standard metadata attributes.
There's a couple of them listed on the slide as examples, but there are many, many more. And when you're writing an importer, you should be sure to support as many of the standard attributes as makes sense for the kind of documents or data that you're indexing. You can also define custom attributes if you have information that you want to index that isn't covered by the standard attributes. It's important to keep in mind that Spotlight works on separate files.
These are the documents in the document-based applications, so great, that's easy. But for Shoebox applications, this requires some thought. If you want to support Spotlight for your Shoebox applications, you need to think about what the individual things are that should be able to be found. Because those things need to be written as separate files.
So for example in Address Book, Address Book saves out each of your contacts as a separate file so that Spotlight can find contacts. Core Data can help you to do this if you're using Core Data to implement your application. It has support for writing out external record files automatically.
So you may keep your data, you know mostly in an SQL Light database, but Core Data can also automatically make sure to write out separate files for each record. And then it provides an API so that Spotlight index importers can access the data that's in those external records for the purpose of indexing.
Now in addition to providing importers for your custom data types, you can use Spotlight within your application. Now this might be as simple as supplying you know a UI that the user can use to do searches. But you can be more subtle than that too if you want.
Maybe you can sort of keep track of what the user is doing, look at the data that they're looking at right now, and sort of automatically kick off Spotlight searches in the background to find related information across the system and bring relevant information to the user's current context directly to them in your app. There's a Spotlight lab happening tomorrow. You can go and talk to the Spotlight guys and ask them any questions. And for documentation, you should start with the Spotlight Importer Programming Guide and the Spotlight Query Programming Guide.
The next technology that I wanted to talk about is Quick Look. Quick Look provides system-wide previews and thumbnails. And it's used in a number of places. So Finder uses it to provide system icons, it uses it for the cover flow view, and both Finder and Mail for example let you preview content directly within the applications using Quick Look. Like Spotlight, Quick Look support is provided by plug-ins.
So your application can provide Quick Look plug-ins for the custom data types that it defines and you can use Quick Look in your application to bring nonnative data into your app and display it to the user. To implement a Quick Look plug-in, you basically, like the Spotlight, you declare the UTIs that your Quick Look plug-in supports. And then you implement two callbacks, one to generate thumbnails, and one to generate previews. For the thumbnail callback, you need to supply an image. This can be a precomputed image or it can be one that you generate on the fly.
For previews you have a few other options. You can supply an image or you can draw it directly into a dedicated graphics context if that makes more sense for your application. Or you can provide your data in a common format like text, HTML, RTF or PDF. These formats are ones that QuickTime knows how to display natively and so it'll handle the presentation if you just return your data in that form.
In addition to providing a plug-in, you can use Quick Look in your application. You can use it to get thumbnails for any file on the system and then present those directly to the user within your application. You can also use it to let the user dive into a full preview of any content directly from within your application.
So think about how these might, you know these things might benefit the users of your app. And especially in concert with the Spotlight integration that we talked about earlier. You know, you can figure out a bunch of stuff that's relevant to what the user is doing, you know bring relevant information from across the system to the user, show it to them as, you know, embedded thumbnails and then maybe let the user dive into a more full preview experience directly all within your application. Go talk to the Quick Look guys in the lab this week and for documentation start with the Quick Look Programming Guide. Next up is iChat Theater. iChat Theater is about presenting data to other people through video chat.
And you can do this with documents or really any content in your application. If your document-based app supports Quick Look, then you actually get a lot of this for free. If you'd like to just have the Quick Look preview presented through iChat Theater, then that just works. You can also support more advanced use cases through a session based API. And that allows you to present any NSView or a Core Video pixel buffer or an OpenGL buffer as well as audio content through iChat Theater. There's an iChat lab this week, so you could go and check that out.
And for documentation you can start with the Instant Message Programming Guide. OK, the last of these integration technologies that I want to talk about is Sync Services. Sync Services is about keeping multiple copies of the same data synchronized in different places. Now most typically this is between your Mac and an iPhone or an iPod using iTunes. It can also be between multiple Macintoshes using .Me or you can synch with other devices using the iSync application.
To support synching the data of your application you need to define a synch schema. And this works best for Shoebox applications where all the data is together and then you know it's usually record based and so you know the user may want to synchronize the contents of their you know libraries across multiple places.
You need to identify what data gets synched and you also need to identify how synch identity is defined for your records. Sync identity is especially important because it allows Sync Services to determine when it's looking at two different records, whether they actually refer to the same thing and that these two records should be merged together. Sync Services has a session-based API that's far too complicated to go into here.
But you can read the documentation to learn more about it. Although I will say that if you're using Core Data, you have a huge head start implementing Sync Services. Core Data has direct integration with Sync Services and allows you to define your synch schema purely by annotating your core data model.
It of course handles local storage of your data for you and it tracks all the changes that are made to the data in between synch sessions so that Sync Services can perform efficient incremental synchs. You can learn more about Sync Services in the lab this week.
[ Cough ]
And for documentation, you can start with the Sync Services Tutorial and the Sync Services Programming Guide. And the Programming Guide is also where you'll find information about the Core Data integration.
Alright, let's review here. We've looked at a number of technologies that can help you make sure that the data the user creates in your application fits into their overall working environment. I urge you to consider how adopting any of these technologies would make sense for your applications and to do so to make sure that you have the best experience possible for your users. Now I'd like to talk a little bit about how you can make sure that your application has the broadest possible audience.
The people who might want to use your application, they come from all over the place. They may speak different languages and have different cultures. Other people who may want to use your application may have disabilities. And still others may look to your application to be part of the solution to a larger problem that they couldn't solve with your application alone.
If you keep all of these folks in mind when you're creating your application, you can make sure that your application may become useful for all of them. We'll talk about a number of technologies that are related to this. And let's just dive right in. The first one is internationalization. Internationalization is all about making sure that your application can be used by people from anywhere in the world.
There are two main facets to internationalization. One of them is localizability and the other one is handling international content. And we'll tackle them one at a time. Localizability is about making sure that your application's interface can be translated into multiple languages. Mac OS X makes this really easy. And if you just sort of pay attention a little bit to this from the beginning of your app development, then it's almost no effort.
Interface specifications can be per region and in fact they typically are by default. And NSBundle and its APIs make sure that when you look up resources, you'll automatically get region specific resources when they exist for your app. Finally, Cocoa has some tools to help you make sure that any text that's in your source code that will end up being presented to the user can also be translated.
Now once you've made your application localizable, of course then you need to decide which languages you actually want to translate it into. And that's a different story. But in order to be in the position to even do that, you have to be localizable. The other aspect of internationalization is handling international content. And in fact this one is even more important than localizability.
There are differences between the different regions of the world. They speak different languages and have different writing systems. People handle time and numbers and currencies in different ways. Now, if the user is able to type text into your application, can they type German? Can they type Japanese? Can they type Arabic? Will you be able to save it properly if they do? Does your application work with dates at all? Does it handle non-Gregorian calendars? You want to think about all of these things. Now, luckily Cocoa actually makes it pretty easy to deal with this stuff if you just use the technologies that it provides.
So all text handling in Cocoa is based on Unicode. And Cocoa has a sophisticated text system that can handle pretty much any language in the world. If you're using all that stuff, then you know it's easy. It'll just work. Similarly there are classes for dealing with dates, different calendars and formatting of numbers and, and other data types.
Use these things and they'll automatically function correctly for the locale that the user is in. Internationalization on the Mac and on the iPhone are actually pretty much the same. And you can see that from the sessions and labs we have. They combine both Mac and iPhone together. You can go to these and learn more about it.
And for documentation you should start with the Internationalization Programming Topics Document. Next up is accessibility. Accessibility is about making sure that your application is usable by people with various disabilities. Now, over a number of years, a lot of research has gone on in the computer industry to help make sure that people with various disabilities can effectively use computers.
And Cocoa has a lot of support for this built right in. To make sure that your application is accessible, there's a few things that you can do that'll be of benefit to anybody who uses your application. Support full keyboard access, this is really important. Don't override system key equivalents especially the ones that are associated with accessibility functionality. Always provide alternatives for drag and drop work flows. And finally in multiple step work flows, if at all possible allow back tracking or canceling out of them.
Again, these things will help anybody to use your application. In addition to that though, you want to keep in mind a few specific things. There are lots of different color blindnesses out there and some of them are actually quite common. So you want to avoid having UI elements that are distinguished only by their color.
Never use an audio cue in your application as the only way to alert the user to some state. And finally when you're designing interactions in your application, avoid ones that require multiple gestures in rapid sequence. Cocoa provides a model for accessibility. And all of the standard UI elements work with that model.
In a lot of cases, all you need to do in your application is provide accessibility descriptions for all of your interface elements that explain exactly what their purpose is within your interface. You can also link related user interface elements together so that accessibility can make those relationships known to the user. If you write custom user interface, you may have some additional work to do.
You may have to expose substructure and other accessibility information about your custom views in order for them to work well with accessibility. And you may need to support relevant accessibility attributes and actions. You can use the accessibility inspector application and voice over to check to make sure that your application is accessible. There's a ton of content this week about accessibility.
So you can, you can go and learn a lot more about it. For documentation, start with the Accessibility Overview and the Accessibility Programming Guidelines for Cocoa. Alright, next up is Scripting. If your application is scriptable, it may be interesting to whole groups of users who wouldn't otherwise be able to use it.
They might use it to construct larger workflows that involve other applications. By using automater or by just writing scripts. They may be able to automate repetitive tasks within your application through scripting and in some cases you might even allow for people to write scripts that extend the functionality of your application.
When it comes to scripting, you want to start early. It's easier to make your application scriptable if you do it from the beginning than it is to try to do it to an already written application at the end. The primary thing that you need to do to make your application scriptable is define a terminology.
The scripting terminology basically describes the way that users talk to your application through scripts, the nouns, and the verbs that are available to them. The terminology should match the user's conceptual model of your application. Scripting an application isn't about scripting the user interface, about writing scripts that click buttons or choose menu items. It's about driving the core concepts of your application.
Now if your internal application model matches up with the way that you want users to think about using your application, then actually this becomes pretty easy. In fact there's a lot of things that will become a lot easier if you can keep those two things consistent with one another.
In that case, all you really need to do to support scripting is to make sure that your model objects are key-value coding compliant. And then implement any custom scripting command handlers for the various classes where they make sense. Again, lots of information about scripting at the conference. Go and check it out.
And for documentation start with the Cocoa Scripting Guide. Alright, the last of these technologies I want to talk about is services. Services are all about making certain features of your application available in the context of other applications. Services can transform data. For example, you might have a service that takes text as input, summarizes it, and then returns a textual summary as output. Or maybe you have one that takes an image as input, runs optical character recognition over it, and returns the text as output.
You can also have services that simply produce data. Maybe a service that takes a screenshot and returns the image as its result. And finally you can have services that simply trigger behavior in your application based on data in other applications. So for example, Safari's Open URL service or Mail's Send Selection service are examples of these.
Services are really great for common data types. Because lots of different applications understand them and so if you can provide a service that works in terms of text and images and files and other things that lots of other applications understand, then you make your application useful in these other contexts as well. Providing services is pretty easy. You need to identify your candidate behaviors.
Mostly you should look to things that your application does that are fairly self contained and that act on input data and/or return output data. Once you've got these, you implement a single object to be the service provider for your application and each service you want to provide will be a method on that object. NSPasteboard is used to communicate data, the input and output data for services.
There's a fair amount of content this week about services and in fact there's been some really exciting developments in services in Snow Leopard. So you should go and check these out. For documentation you can start with the Cocoa, sorry with the Services Implementation Guide. Alright, there's a few other sessions that didn't really fit into the other you know specific topics that I wanted to point out.
What's New in Cocoa actually happened earlier this morning, but if you missed it you can catch it on iTunes later. The companion session to this one which is all about user interface design is going to happen in this room right after lunch. That one I really recommend. And then there's the Cocoa Tips and Tricks session, which happens a little bit later this week.
Alright, so we've kind of had a fast, you know fast paced overview of a lot of technologies in Mac OS X today. And we haven't even hit all of them. It's actually a challenge to know all of the things that are available for your application to take advantage of in Mac OS X so that you can know which ones are appropriate for your application to use.
We hope we've given you a bit of a help today in knowing what those things are. And we hope that we may have given you some ideas for how you can use them in your application. But most of all we hope that you all go out and you write really great Mac OS X applications using these technologies so that we can use them. All right, that's it.