Application Frameworks • 50:30
This session discusses the new Pasteboard and Translation Services APIs and how they leverage the Uniform Type Identification architecture, a new way to represent type information. We detail how you can extend the new translation mechanism to offer custom translations. We explain how using scrap and drag APIs can adopt the new Pasteboard Services to simplify and enhance your app's data exchange features.
Speakers: Christopher Linn, Bryan Prusha
Unlisted on Apple Developer site
Transcript
This transcript was generated using Whisper, it has known transcription errors. We are working on an improved version.
Data Exchange. So what do we mean by data exchange? As Xavier mentioned, we're talking about in today's Mac OS X and earlier Mac OS, some technologies such as the Scrap Manager, which we've had since 1984 for doing data exchange, moving data between applications on the clipboard. A few years later, we introduced the Drag Manager, which is a similar API in many ways for moving data between applications, but through drag gestures. We're also talking today about the Translation Manager, which was also introduced several years ago and integrated into scrap and drag so that translations can be performed somewhat automatically when moving data between applications.
And all of these technologies are based on OS type data identifiers, four character data identifiers. And all of these technologies have served us very well. We've made a lot of strides in user interface in making it easy for users to move their data between applications. But there are also some limitations, some problems, that we would like to get past going forward. We have a lot of ideas for enhancing the user experience and being able to make applications work together more seamlessly.
And we're bumping up against some problems. One is just that the scrap manager and the drag manager, although they have a lot in common for marshalling a bunch of data together to move to another application, they're separate APIs. And so if you want to support the clipboard, you do one thing. When you want to add drag and drop support, you have a whole other similar API that you also have to adopt. So it's kind of double the work for you. Thank you.
With the Translation Manager, it's kind of a difficult API to use. The model isn't really quite what we want for data translation. It's handle-based, so it doesn't really work with core foundation collections and so on, which make memory management much easier. And so translation just isn't really working quite right.
And then finally, the whole issue of type identification. As I said, the Carbon technologies tend to be OS type-based. Cocoa uses strings. NS Pasteboard uses strings for data identifiers. And then there's also MIME types coming in over the internet. There's file name extensions. So all these different ways of identifying types. And there's a number of issues with that that we'd like to resolve.
So what are the solutions that we're going to be talking about today? The first one is the Carbon Pasteboard. This is a new technology, new API, which is a complete replacement for the Scrap Manager and also a replacement for large pieces of the Drag Manager. So it unifies that data model for moving data around between apps.
And then there's a new API called Translation Services, which is integrated into the Carbon Pasteboard and improves the translation architecture to make it easier and faster to work with when you're developing your applications. And underneath those technologies is something we're calling Uniform Type Identifiers, which is a new way to describe type information and gives a very rich model for managing type information.
And it's a foundation both for translation and Pasteboard, as well as future technologies that we're hoping to introduce. So since it's really the foundation, we're going to talk about Uniform Type Identifiers first, and then later on, my colleague, Bryan Prusha, will come up and talk about Carbon Pasteboard and Translation.
So in order to help you kind of understand the concepts of Uniform Type Identification, I wanted to start with an example of, I just picked a type, TIFF, TIFF images. How do we know what a TIFF image is? What's a TIFF? I'm not talking about what's the format, but how do we identify TIFFs? And the first one that you might see is the OS type TIFF. It's what a lot of Carbon apps would work with if they were putting TIFF data on the scrap.
But there's also a MIME type. So an image coming in over the internet would have the image slash TIFF MIME type. There also happens to be a file name extension, .TIFF. There's another file name extension, .TIFF, which you have to recognize. Your app working with TIFF data or TIFF files has to also recognize that file name extension.
And then data put on the Pasteboard by a Cocoa application would use a string, NSTIFF_PBOARD_TYPE. So there's kind of this whole cloud of type identifiers. And currently on Mac OS X and previous macOSes, the only facility really for tying these together at all was Internet Config, which was very internet oriented. And its API is also pretty fixed in terms of the type of the interface.
So it's not just a type of the interface. It's a type of the interface that supports, for instance, it doesn't understand anything about
[Transcript missing]
In the example here, the identifier that we're talking about is public.tiff. And this is the Uniform Type Identifier for the Tiff data type.
And I like to think of The new Type Identifier is the one true name for the type on the system. To use a literary analogy, it's one name to rule them, one name to find them, one name to bring them all in the darkness, bind them. Not only does the Uniform Type Identifier identify the TIF type, but the API makes it very easy to discover all of the other type descriptors for this type. It's kind of the nexus for type description.
So what about that public thing up front? Well, unlike MIME types, Uniform Type Identifiers don't actually express any sort of type hierarchy in the name. So the prefix on any type identifier is, in general, a reverse DNS name. We're reusing the reverse DNS naming convention that is in use in several places on Mac OS X, such as in bundle identifiers.
So type identifiers that are proprietary or associated with a particular organization are prefixed with that organization's reverse domain space. However, there's a lot of types that really aren't owned by any particular organization, and those go in the public domain, which is, at this point, controlled by Apple. I encourage you not to use the public domain and let us put things into the public domain.
Now, because of this naming convention, I mean, Uniform Type Identifier really is just... It's just a string, and the format is a naming convention that we're asking everyone to use. If you follow the naming convention, you're pretty much guaranteed to not conflict with anybody else, A. And B, it's a very descriptive string for what the type is.
It tells you not only at the end, it can describe something about the type, but it indicates what organization actually owns and defines that type. And so it gives you, as a developer, when you see some Uniform Type... Identifier coming at you from another application, you have an idea of where to go to learn more about that type.
Throughout the rest of the talk, we might be referring to them as UTIs, Uniform Type Identifiers. That's the abbreviation. So what are some examples? In addition to public.tiff, another one is a little more verbose, public.utf.plaintext. So that's an example of a very descriptive type. You can pretty much look at that and figure out what it is. It's a Unicode UTF-16 encoded chunk of plain text. In fact, this is the type that we encourage everyone to start using with the Carbon Pasteboard when you are sending plain text to another application.
Here's an example. Picked, a quick draw picked is something that is specific to Apple and so we put it in the com.apple space. Adobe might, they haven't done this, but just as an example, they might for Photoshop documents use com.adobe.psd or they might use something more verbose like Photoshop document.
So in addition to describing, identifying types specifically, there's something else which the system has never done before to model our understanding of types that we all have as developers and as users, which is what else do we know about a TIFF as humans? We know that it's an image, and we know that it's a chunk of data.
And what the Uniform Type Identification system does is allow types to exist in a conformance hierarchy. We can declare types as conforming to supertypes so that even if your app doesn't know anything about a very specific type that you receive, you can discover that, oh, this type is actually a form of text, or it's a form, or it's an image.
That example was somewhat trivial, but a somewhat more interesting example is receiving something that I'm not even going to pronounce because every time I did during rehearsals, I sounded silly. But this is an example of something you have no idea what it is, and it arrives at your application through a drag gesture, say, and you would think you can't do anything with it.
But if you happen to be an XML parser, you can say, is this type XML? And it turns out it does conform to XML. Or is it even generic text? And you can discover from by asking the system that it does conform to text. And so you can actually work with this data, even though you don't know the complete specifics of its format.
So in those last few slides, then, I've just outlined all the major goals of Uniform Type Identifiers to kind of tie together all the disparate type identifier spaces, model conformance between types, Enable extensibility through new third-party type declarations without needing a registry or having to worry about collisions. And in support of all that, just provide a very simple, lightweight API, which is a foundation for any component or code that needs to do type management.
So now I'd like to go back and just go through the same concepts with a little more detail about some of the API behind this. When we talk about OS types and MIME types and extensions and NSPasteboard types, we needed a word to describe all of that, so we just kind of picked this one, tags. What we have, that cloud of blue identifiers are all tags that are related to a particular Uniform Type Identifier. And each tag has a class. The class indicates if it's an OS type or a file name extension or a MIME type, etc.
And the thing to remember about tags is that there's not always a one-to-one mapping. Usually there's a one-to-one mapping, but not always. And so some cases are complex. You have to map multiple file name extensions back to the same Uniform Type Identifier. Sometimes the same extension or OS type may have a collision and may map to two different type identifiers. So our API makes it easy to deal with the common case of a one-to-one mapping, but it also allows you to... enumerate all the different combinations so that you can discover more complex type relationships.
So here's that cloud again of types, and the API lets you easily map between the tags around the outside and the one true type identifier in the middle. And so that makes it very easy to go through kind of a two-step translation where you get to the uniform type identifier, and then you want to know what the tag is in another space, and you can go from there to the tag.
And this is actually used probably more heavily in the system than you may have to use it because we have to deal with Cocoa Apps and file name extensions in the file dialogs and We're going to talk about the OS types in the Scrap Manager, and we need to make all those systems work together, especially moving data between the NS Pasteboard and the Scrap Manager. And so we rely on this internally to do all of our mapping between namespaces.
And how do we do that? It's very simple. This is just a trivial example of taking the OS type JPEG, which is treated as a string in the API, and you call UT type create preferred identifier for tag. You pass in the tag information. You say that it's an OS type tag, and you get back the type identifier for JPEG, which happens to be public.jpeg.
Then if you want to go to the MIME type for that, you would just do another-- it's another string operation, UT type copy preferred tag with class. And you say you want the MIME type class. And you get back the string, which is image/jpeg. So it's a very simple mapping process.
Now onto the conformance model and testing types for equality. So this is just a simple hierarchy of type identifiers. We've got HTML and XML as generic text types, and then XML includes com.apple.propertylist on your right, and XHTML is a subtype of XML. And let's say you're receiving some data from another application and you There it is.
You handle XML. You're some sort of XML processor. You want to find out if the data is XML. You can ask, does the data that I'm receiving conform to XML? And if the type identifier that you're receiving is XHTML or com.apple.plist or just plain XML, then you get back the answer, yes, this is data that I can deal with.
In other contexts, you actually do want to test for specific equality of type information, and we provide an API for that, which fundamentally uses a string comparison to do the equality. But there are cases where a string comparison isn't enough. Here are those APIs. Again, very simple. Just two input strings, returns a Boolean.
And I wanted to say a little bit more about why the equal... Why the equality test is there? Obviously, to do conformance testing, you have to resort to calling that so that we can use the type information in our database to give you the right answer. As for equality, well, we do use a string comparison internally to test equality. It's case insensitive since these are like DNS names. UTIs are all case insensitive.
But we also expect in the future that, you know, as carefully as we've tried to design the naming conventions for types, we're expecting that little warts will happen. There will be a type identifier which is in somebody's or some organization's domain and somebody else will put it in the public domain or Apple will want it in the public domain. And if you use this API to test for conformance and equality, we'll be able to do things like declare two type identifiers as synonyms. and everyone will just continue to work if you use our API.
So I want to talk a little bit about declaring new types. I don't want to go into the details, but there are some samples I can point you at. So new type identifiers are declared in bundle property lists, in your application's bundle property lists. In fact, all of our built-in identifiers are declared currently, anyway, in the seed in systemicons.bundle, down in system library core services. So you can go there to find an example.
It's very similar to the, if you're familiar with declaring document types in your P-List, declaring the types of documents you can open, it's very similar to that. We've kind of taken that idea and abstracted it out to just deal with type information so that you can declare in your P-List what your type identifier is that you want to use and what the equivalent tags are so that you can associate it with MIME types and file name extensions, as well as what it conforms to. Is it an image? Is it text? or something more specific.
And once you do that and your app becomes registered on the system, we add that type information to our database and it's available globally so that when you send your data to another application with your specific type identifier attached to it, that application can use our API to discover things about your data.
The last thing I wanted to say about it was that there's actually two ways to declare a type. One is called exported and one is imported. It's a little subtle, but it's necessary. In general, for any type identifier that you are giving out to the world for your data type, you export it from your property list. You make an exported declaration.
If you're using a built-in type, such as any of the public types, then you can rely on that always being declared in the system because the OS guarantees that it's there. But sometimes your app might depend on some third-party type that has been declared in, say, a Microsoft application. You want to import Word documents, and Microsoft has declared in their bundle a Microsoft Word identifier.
You depend on that declaration. You depend on the system knowing about that UTI. But you don't know if Microsoft Word is installed on the system you're running on. In fact, you probably hope it isn't installed because you want to read those files and provide services for importing Microsoft Word documents. In that case, you can actually re-declare Microsoft's definition or declaration of their type.
That's called importing. You say, I'm importing this type declaration, which really tells us that you're not the definitive owner of that type. And if Word is installed or the third-party app is installed, we'll use their declaration. But if you want to ensure that a declaration is there, then you can use that style of importing.
So that is really an overview. I didn't cover all of the API. As I said, there's additional functions for dealing with ambiguities and mapping between different types. But it's a conceptual overview. And what are the guidelines for working with these? Again, we provide tests, especially when working with Pasteboard data and drag data. You want to use -- these new APIs use uniform type identifiers. And so you want to use the conformance and equality test when receiving data to find out if it's data that you can accept.
Wherever possible, use the built-in types that we're providing. And when you do need to declare new types, just make sure you pick an identifier that's in your organization's domain space so that you're guaranteed, as long as your own organization is coordinating within itself, you're guaranteed to have a unique identifier for your data.
This is all implemented as essentially an extension of the Launch Services database. And so in the seed anyway, the API and extensive commentary and documentation lives in launchservices.h. It's likely to get its own header file before we go GM, but that's where you can look in the seed for more information. So that is the foundation for type management. And this brings us to Bryan's part of the talk, where he'll talk about how the Carbon Pasteboard leverages type identification.
All right, thank you, Chris. Chris has just laid a great foundation for these two new APIs with Uniform Type Identification. Now I'd like to introduce the Carbon Pasteboard and Translation Services, which will leverage this foundation. First, I want to talk about Data Exchange today a little bit more. As Chris mentioned earlier, we have separate scrap and drag managers. They do basically the same thing. You put information on a scrap or a drag on one application, you pull it off on another application.
This is a little redundant. This not only means that you have to pay attention to two APIs, then you have to do twice the work in your app. So you have twice the amount of code. And these involve different models. The scrap manager only has a single item. Drag manager has multiple items. The way that you provide data or provide promises and fulfill promises are different. So it's really kind of a pain to keep in mind which model you're in.
Also, as Chris mentioned, there's a limited typing mechanism. They're both based on OS types. And you can have collisions. You need to contact Apple and make sure that you register your types. And this is difficult and something we wanted to move away from. and currently there's pretty much non-existent communication with Cocoa. You might think, well, I can copy and paste between Cocoa and certainly they can communicate, but this is something that's handled by us internally. We only do it for a few standard text and picture types.
And so there's really no way for an OS-type based API to communicate with a string-based Cocoa API. And the memory model is fairly labor intensive. What I mean by this is that when you want to pull information off of a scrap or drag, you have to ask how large the information is.
And then once you have that, create your own memory, then ask for that memory to be filled in, and then release that later. So we wanted to get away from each of these issues. Now the solution to that is the Carbon Pasteboard. The Carbon Pasteboard is the unification of the scrap and drag flavor APIs. So it's a complete replacement, and you can move away from them today.
The Carbon Pasteboard supports copy and paste, drag and drop, and services, both Apple menu services and translation filter services, which I'll get into later. We really like the model of having multiple items, each with their own set of flavors that the drag manager has. We think this is very flexible and we wanted to bring that forward. Flavor types are now based on Uniform Type Identifiers. One great thing for scrap and drag means that because it's based on UTIs, you can now communicate with NS Pasteboard types.
You can now convert the UTIs into UTIs and then you can pull them off as Uniform Type Identifiers and then convert them into any type you like using the UTI API. and the Pasteboard, Carbon Pasteboard is core foundation based. The Pasteboard Reference itself is a core foundation type and so you'll want to use the, you'll want to release them after you've created them. But the entire, throughout the API, it's based on core foundations.
You'll see CFStrings, dictionaries and data. All right, let's talk about the base type that you'll be dealing with Pasteboard, the Pasteboard Reference. A Pasteboard Reference is a proxy to a global Pasteboard resource. You can have multiple references, a reference in multiple applications to a single global Pasteboard. That's kind of the point so you can communicate between the two. You can have multiple references within a single application. So if you have multiple objects within your application, each one of them could have a local Pasteboard ref.
As the number of references on the system within multiple applications increases, the ref count on the global Pasteboard resource increases. And so as each of the references are released through CF release, then the references on the global Pasteboard release, once the last application has released its reference, the global Pasteboard memory goes away. Now there are two exceptions to this, which I'll mention in a moment.
[Transcript missing]
Now that we've created a Pasteboard, you want to access that Pasteboard to do certain operations with it, typically reading or writing. So the first thing I'm going to talk about is reading from the Pasteboard. Before you want to read from the Pasteboard, you want to check out and see if any other applications have modified it. So you can call PasteboardSynchronize, which returns a set of flags indicating whether the Pasteboard has been modified.
So if some other application is in the front, the user has performed a copy, your application has been brought to the front, and you're curious as to whether you want to enable paste or not. You can call PasteboardSynchronize on the clipboard, the clipboard pasteboard, and it will return a set of flags indicating whether it's been modified or not. If it has, then you can go ahead and look at the Pasteboard, see if there are any tasty flavors on there. If so, go ahead and enable your paste item, paste menu item.
Now if you're going to write new information to a Pasteboard, you want to make sure that you clear it first. Clearing the Pasteboard wipes out any data that any other application has written to it and makes your application the owner. So you are the only one who can write to it at that time. So the two rules here are always sync your Pasteboard before reading data from it and always clear the Pasteboard before writing to it.
There are two branches I could take at this point, either writing or reading data. I'm going to take the reading data branch first. When you're receiving data from the Pasteboard, the Pasteboard has multiple items. You want to get the number of items through Pasteboard Get Item Count, and then we access each of the items through a unique identifier. It doesn't matter what the identifier is. It could be 1, 2, 3, just indices, or it could be a pointer to your data in memory.
This is actually very similar to the way the Drag Manager works today with multiple items. So if you're used to that model, most of you should have some drag code or copy and paste code in your application. So a lot of this will look very familiar. Thank you. Okay, once you've looked at the first item, you can enumerate the items. You want to look at all the flavors that are inside.
So unlike the drag manager, which required you to not only iterate through each of the items, but then iterate through each of the flavors, we'll pass all the flavors to you at once in a single API, Pasteboard Copy Item Flavors. It passes back a core foundation array. Each of the types on the Pasteboard are uniform type identifiers.
So you'll want to make sure to use the comparison routines, especially when you're reading the data, especially conformance. Ask for the data in as general a way as possible. If you're a general text editor, first you want to ask, is text available on the Pasteboard? And then you can be more specific after that if you like.
One important thing to notice, translations are back with Translation Services. And the translations are always offered after the sender flavors. So ordering is important when pulling flavors off of the Pasteboard. When you're done with the flavor array, you'll want to release it. Okay, now you've got a bunch of these flavors, but you want some more rich metadata associated with these flavors. So you can grab the flavor flags via Pasteboard Get Item Flavor Flags.
Now, these are almost exactly the same sort of flags available in Drag Manager today. This is for compatibility, with one exception. There's a request-only flag now that behaves very similarly to the sender-only flag of yesterday. It behaves a little like having an unlisted phone number. So somebody trolling through the phone book can't find your phone number, but if you share it with your friends, then you can call each other.
And so this is very nice if you have a suite of applications and you want to communicate between them, but you don't really want to announce certain data types out to the world. And so any flavor that you add to the Pasteboard that has a request-only flag set on it will not be advertised in the previous API that returns all the flavors on the Pasteboard. But if you know the flavor name, you can go ahead and ask for it explicitly and then get its flags and get the data. So this is a nice addition that I think you'll appreciate.
Okay, once you've looked at all the flavor flags, you've looked at the flavors, paid attention to their ordering, there's a rich amount of information there that you want to pay attention to. But you've decided you've got the flavor that's right for you, and you want to get the data.
The single API you want to deal with at this point is Pasteboard Copy Item Flavor Data. Again, it's based on CFData, and so you'll want to release the memory when you're done with it. But now you don't have to do any allocation, you don't have to ask how big the flavor is, it's very simple.
Okay, now let's back up a little bit. I mentioned that there are two branches we could take. Let's go down the other branch where we can add information to the Pasteboard. When you add data, the first thing you want to do is add data in your application's order of preference in richness.
Now, that seems a little odd. You'd think you want to add it in the order of richness that a receiving app would prefer. But really, when you're initiating a drag or putting information onto the Pasteboard for a copy, there's no way to know what the receiver is going to be.
And so you can't anticipate what order they will want the information in. So the best thing you can do is indicate this by adding information in your application's order of richness as just an indication saying, "You might want to look at this item first." In order to add a promise, you can pass null as the data or the KPasteboardPromiseData constant. Now, this is very convenient. There's just one way to add data, one way to add promises. There's no longer two separate models between scrap and drag. Once you've added data, you want to fulfill this in a promise keeper, so keeping promises.
You have to install a Promise Keeper before making promises. The Carbon Pasteboard makes sure that you are able to fulfill the promises that you add because it wants to guarantee that receivers of that information can get access to it. In order to keep the promise, some application has requested that your promise be fulfilled and your Pasteboard Promise Keeper proc will be called back. You'll be given the Pasteboard that the promise was made on, the item that it was made within, and the flavor type being requested, and any context information that you've passed through.
So when the promise is going to be kept, just call Pasteboard put item flavor data as if you were not making a promise to begin with. Very simple. Again, it's just a single API for adding data and fulfilling promises. So promises-- one cool thing here is that promises are resolved when you release your Pasteboard ref.
So if you have, again, a plugin architecture, and each one of your plugins has its own Pasteboard reference, and they can each make promises to the global Pasteboard independently of the others. And so if one plugin is brought into memory, it adds a promise to the Pasteboard. But for some reason, it's going to be moved out. You're dropping that plugin. Then when it releases the Pasteboard, only its promises will be explicitly called in. All the others will remain. So this is a great way to allow promises in a plugin architecture.
All right, I mentioned that this is a replacement for the Drag Flavor APIs. There are a couple of additions to the Drag Manager. You can see some HeaderDoc on them in drag.h in HIToolbox. You can use the Pasteboard with the Drag in two ways. Either create a drag and pull the Pasteboard out of it. This is useful if you're receiving a drag and you want to use the Pasteboard API.
Then you can just, when you're given the drag ref, pull the Pasteboard out of it. Or, depending on the flow of your application, if you're using the same code to perform copy and paste and drag and drop, maybe you have a Pasteboard that exists already. So you can call new drag with Pasteboard, and a new drag will be created with the Pasteboard that you've provided.
A couple guidelines now for using the Carbon Pasteboard. Always pay attention to the flavor flags and ordering. If you're receiving information, you want to take advantage of all the information that you have to choose the right flavor. We've had issues when trying to add some default translations that applications will see a translated flavor and think that that's the one they want in a given context. In fact, the context that you'd be pasting or dragging into might not even be applicable for that type. So it's very important to take all the information that you have and make the best decision you can.
Always use UTI comparisons for the best results. You want to leverage that API to get the most coverage you can in receiving data. One note about the WWDC seed is that NSPasteboard types are added by Cocoa as NSPasteboard types. They are not yet converted to Uniform Type Identifiers. Their existence is only temporary. They will be going away. They will be converted to Uniform Type Identifiers before Panther ships. So I don't want you to see them and depend on them. So pay attention to that being there. This will change for Panther.
And again, there's plenty of header documentation in Pasteboard.h. This is located in hiservices in the Application Services Framework. There's HeaderDoc there. There's a couple features that I wasn't able to get into here. Go check it out for details. Also, there's some sample code that we whipped up for the conference, Pasteboard Peeker.
It's kind of a cool little sort of text editing app that shows you everything that is on a drag or in the Pasteboard. You can add information to a drag or Pasteboard. And it exercises the API and shows you how you can use it. That's on the ADC member site. Go check it out. All right, that's the Carbon Pasteboard. Let's get on to Translation Services. Thank you.
Let's talk about translation the way it is today. Again, there are a set of problems that we've discovered, and we really wanted to solve these. Again, there's a limited typing mechanism with four character codes, OS types. And we've gone over this several times. It's also a legacy API.
For instance, it's based on FS specs, which have no Unicode support, and so we couldn't support the full type of -- full number of -- of files that we want to support on Mac OS X today. In fact, with the memory model, it's still based on handles, and it's still based on C arrays of all things. If you wanted to get some information in an array back from the translation manager, you'd have to pass through a C array.
And if you didn't allocate enough space, too bad if it wanted to give you more information than you had asked for. And the third thing, and really one of the biggest things, is that the way to extend translations in Mac OS 9 and earlier, the plug-in mechanism was based on extensions. And extensions simply don't exist in Mac OS X today. And so this is an issue that really needed to be resolved. And the solution? Translation services. So translation services is the successor to the translation manager.
The source and destination types are both provided as Uniform Type Identifiers, so very flexible. It's extensible via filter services, which I mentioned earlier. This is something else, actually, that we've adopted from Cocoa. They're very similar to Apple Menu Services with a modified plist, and I'll get into that later. And the entire API, again, is core foundation-based, so it has an advanced memory model.
So a translation reference. A translation reference is the base type you'll be dealing with. It's a proxy to a resource on the system that translates data from one format to another. It contains all the information that's required to perform a translation, the source type that it handles, the destination type it will provide, and whether it handles file or data translations.
And in the Translation Services model, translation discovery and execution are separated. And so that's important because you can create a translation if you know you're going to be referencing that translation many times, translating many files or something, for instance. You can hold on to that. Because it's a CF type, you can throw it in a CFArray or a dictionary and keep it around until later.
So let's talk about discovery. Translation Create. Translation Create is a pretty simple, straightforward API for finding a translation you might need. Let's say you have a source file. You have point A here, and you want to get to point B. You know that there's a destination you want to get to. Does a translation exist that supports that? So you can hand the source type, the destination type, and searching for data and file translations is performed through the same API now.
And so you can pass through the translation flags, whether you want a translation that handles just data, just files, or maybe both. And then if the resource exists that performs this translation, the translation reference is handed back to you. So that was for very simple needs. If you have more detailed or complex discovery needs, you can use Translation Create with SourceArray. This will handle any permutation of translation discovery that you need.
Given an array of source types, it will provide to you all the possible destinations that can be generated from those source types and a dictionary of translation refs which perform those translations. The dictionary of translation refs is keyed by the destination type, and so it's easy to do the lookup. Again, when you decide on the translation that's right for you, you need to release the CFArray and the CFDictionary.
Now, how do you discover which translation, out of all those translations, which one is the right one for you? Well, you can use the translation accessors. You have access to the source type, the destination type, and the translation flags that it supports. We hope to add more accessors in the future when more information is provided.
This is a way you can go through and decide that I really want to get to this destination type and You really want to get to one destination type, but I'd prefer it coming from this source than this source, because maybe this one is a little bit richer. You can really get fine detailed if you want to.
All right, you've discovered a translation. You've created it. You've got it in your hot little hand. And you want to now perform a translation. You want to execute a translation. So if you want to perform a data translation, you can call translation perform for data, hand it to the translation ref, hand it your source data as a CFData. And then a CFDataRef will be given back to you holding the destination data. Read that data out, do whatever you want with it, and then release it.
There are a couple things you need to pay attention to. Make sure that when you're handing a translation ref to translation perform for data that it actually handles data translations. Otherwise, you'll receive an error immediately. Also, something that you want to make sure is that the source and destination types in the translation ref are the same source and destination that you're providing and expecting from this API.
Very similar for translating files, call translation perform for file, pass through the translation ref, your source file, you can provide the destination directory and name for the destination file. And then the API will return an FSRef of your translated file. And again, you need to make sure that the translation ref supports files and that you have the correct source and destination types. If your application happens to not be FSRef based or your translation code is not FSRef based, you can use the URL API. Currently, only file URLs are supported. And again, when you receive the URL to your translated file, release the CFURL reference.
I've mentioned filter services a couple times along the way. These are the extension mechanism for the default translations. These are very similar to Apple Menu Services with a modified plist. So if you've written one of those, these will be very familiar. I don't want to get into too much detail here. The Pasteboard Peeker sample, again, that I mentioned earlier, down at the bottom of the slide, has an example for you.
But when you're going to perform a translation, your service is launched, if necessary, if it's not already running. The source data is delivered to you on a Pasteboard. You can pull that data off, perform a translation, and then put the destination data back on the Pasteboard, hand it back, and the client can have access to that data.
All right, a couple guidelines for Translation Services. Use Translate Create for very simple needs. If you have one or two types that you're interested in for a given source type, you can just call it a couple times and ask, does it handle this flavor? Can I get this flavor? Can I get this flavor? Oh, I can get this one.
Great. If you have more complex needs than that, call Translation Create with SourceArray. And again, it'll handle any permutations you have. Definitely check out the TranslationServices.h header for details. Lots of HeaderDoc there again. There's lots of examples there for how to create a translation service and so on. So go check it out in HSServices in the Application Services Framework.
All right. In summary, we've come up with three great new APIs that I think will -- I know they definitely help us internally, and we're really hoping that they'll be great for you. What we need to do now is, you know, you've got the seed in your hands. Get to work. Uniform Type Identifiers are a really flexible framework for typing.
If you have data that you need to type, it's the way to go. Always declare new types in your Internet domain so that if they are your types, you own them. otherwise import them from other applications. and always use the UTI API for conformance tests. This is your mantra, because you want to make sure that you can really leverage the full flexibility of the Uniform Type Identification API.
Pasteboards can really streamline your scrap and drag usage. I was coming up with that Pasteboard peeker sample and it was really cool just how simple. I could use the same function to read data from both drags and copies and the same function to add information to drags and copies. Really convenient. Now that you have access through UTIs to Cocoa applications, look at Cocoa applications that your app might be able to correspond with and really see how you can leverage Cocoa app usage.
And then always be aware of automatic translations. They're there to help you, but make sure that they're not getting in your way. Make sure that you're looking at the flags to see whether it's a translation or not and decide whether you want to have a translation or not. And update to translation services. Translation Manager is frankly unimplemented on 10. We wanted to make it happen, but because of the reasons I stated earlier, I knew that we could create a much better solution for you. And use filter services to extend the default translations.
There's only a limited set that Apple will be able to provide. There was a really rich set of translations available in the Mac OS 9 world. We'd really like to see those brought forward to Mac OS X. So check this out. This is an excellent third-party opportunity for all of you. So with that, I'd like to bring Xavier back up for the wrap up. Thank you.
OK, quickly, the roadmap before we go to the Q&A. Session 4.9, the HIToolbox was Wednesday. Some of you can travel in time. I encourage you to look at that session. It was pretty good. And if not, we'll have it on DVDs. Using the Apple tools for Carbon development, same thing. That was just before. But Apple has been investing a lot of time and effort in Xcode, and I encourage you as well to check it out.
You won't have to travel in time to see session 416, which is optimizing performance on Presidio. It's a great, great session for anybody that is doing C and C++ development on our platform. We're going to be going through a ton of information. We're going to give you ideas on how to improve performance on your app.
Please join us this afternoon. And tomorrow, of course, if you haven't heard, we have a brand new H.I. Toolbox available in Mac OS X. Since 10.2, we introduced H.I. View. And should you need more information on this great technology please join us tomorrow afternoon All right. Should you... Did you change that? You're so fired. You are so fired.
Anyway, we'll fix that later. Should you have any questions on the new Pasteboard Services, please send me an email and we'll try to get you going. It's very important to understand that the idea here is we need to have your feedback. So please check out the APIs and contact us should you have any specific needs. Or maybe we forgot something, or maybe there's a special case to make you run that we need to implement. And that's the idea with having WDC and giving you the seat of painter.