Configure player

Close

WWDC Index does not host video files

If you have access to video files, you can configure a URL pattern to be used in a video player.

URL pattern

preview

Use any of these variables in your URL pattern, the pattern is stored in your browsers' local storage.

$id
ID of session: wwdc2006-106
$eventId
ID of event: wwdc2006
$eventContentId
ID of session without event part: 106
$eventShortId
Shortened ID of event: wwdc06
$year
Year of session: 2006
$extension
Extension of original filename: mov
$filenameAlmostEvery
Filename from "(Almost) Every..." gist: ...

WWDC06 • Session 106

Taking Advantage of Leopard Features in Cocoa

Application Technologies • 1:04:38

Gain an in depth understanding of Cocoa support for major Leopard features. Find out how to prepare your application for resolution independence, how and why to convert your application to 64-bit, and how to use the Cocoa API to incorporate other Leopard features into your application.

Speakers: Kristin Forster, Ali Ozer, Mark Piccirelli, Kevin Perry

Unlisted on Apple Developer site

Transcript

This transcript was generated using Whisper, it has known transcription errors. We are working on an improved version.

I'm Kristin Forster, I work in the Cocoa Frameworks group and I'd like to welcome you to session 106, Taking Advantage of Leopard Features in Cocoa. Today we're going to dive into three of the features that Chris covered in his What's New in Cocoa talk. We're going to talk about the benefits of these features and we're going to show how to adopt them using TextEdit as an example. We'll show some problems you may run into and talk about how to solve them. And our goal is that you leave this session ready to take advantage of Leopard features in your Cocoa application.

First, Ali Ozer is going to talk about 64-bit. Some 64-bit libraries are available on Tiger. On Leopard, 64-bit has been brought to the Cocoa frameworks. With a fairly mechanical conversion, you can make your application 64-bit aware, and we provide a top script to make this easy. Once you have done this conversion, you can take advantage of 64-bit capabilities in your application.

Next, Mark Piccirelli is going to talk about uniform type identifiers. Uniform type identification is a type system that encapsulates file name extensions, MIME types, OS types, and other information. You may already be using uniform type identifiers if you've written a spotlight importer for your custom document format. On Leopard, you can use uniform type identifiers throughout your application to simplify your type management and to improve your interoperability with the rest of the system.

Lastly, Kevin Perry is going to talk about resolution independence. Resolution independence allows your user interface to be drawn at the same physical size regardless of the pixel density of the display. We had some support for resolution independence in Tiger. In Leopard, we have improved and extended this support and most notably, most standard user interface elements are now drawn using resolution independent artwork. Now I'd like to invite Ali Ozer up on stage to talk about 64-bit.

Good morning. So 64-bit is one of the major big things that we've done, of course, throughout, across Leopard, and Cocoa is, of course, also one of these areas where we did a lot of changes. So let me first define what 64-bit is. 64-bit, the most fundamental definition of 64-bit is support for 64-bit address space. 32-bit supports 4 gigabytes, and 64-bit supports 4 billion times that.

And, you know, if you were to have one fundamental definition, it's 64-bit pointers. That's what 64-bit means. 64-bit is very interesting for applications which need more than 4 gigabytes of address space. These are applications that have already been asking for 64-bits for some time now, and clearly they're a good target group to move over. But it's also potentially interesting for other applications as well. Now I'm not going to go into details or motivations for that now, but this afternoon, 2:00 p.m., after lunch, in Marina, 64-bit overview is where we'll cover this topic in more detail.

64-bit uses the LP64 data model. Simon mentioned this. It's longs and pointers change to 64-bit. So pointers are 64, the long data type is 64, and all other data types remain the same. So here's a handy little chart showing you what happens. And again, you can see everything remains the same. And one interesting item here is the fact that the int data type, which used to be 4-byte, is still 4 bytes.

Now, in Cocoa APIs, as most of you are probably aware, integral values are almost always int or unsigned int. And this includes APIs which should support 64-bit quantities in a 64-bit process. This includes things like NSData. You know, you really want NSData to represent files larger than 4 gigabytes. Same with, in fact, array and string as well. These methods, instead of returning ints, you really want them to be 64-bit capable.

So one possible solution here is why not change ints in these APIs to longs? In that case, an SRA method such as count would start returning unsigned long, and object index would start taking unsigned long as well, and that would make them 64-bit capable on a 64-bit environment. Now, however, that's not what we did.

The major reason we didn't do this is the fact that if we change ints to longs in all these APIs while keeping the APIs the same, this might have adverse effects on existing 32-bit applications. It's very important that we maintain binary compatibility as we change stuff. And if we change ints to longs in methods, although that's totally compatible in the 32-bit world from a size, you know, 4 bytes, 4 bytes, it does change the method signatures in the runtime to some degree.

It doesn't actually change, you know, the method name, but the method metadata. It also would require some source code changes on your part. So even though you might not care at all about 64-bit, by compiling under Leopard, you might find yourself having to change your source code to accommodate this.

So we decided not to do this. So we leave 32-bit as is. Now what we really want is something like this. If we're compiling for 64-bit, we want the count method to return a long, and if we're compiling for 32, we want it to return an int. Of course, this solution.

Where we do this per method is very unwieldy because, you know, depending on how many methods we do, you're going to have all these alternate declarations for every one of them, and that's going to get messy. So, you know, it's not exactly. We don't want to do exactly this, but in spirit, this is what we want. So we introduce two new types and NSInteger and NSUInteger.

And here are the way they're defined. Now first thing to note is this underbar underbar LP64. This is the way we know that we're under the 64-bit environment. If we're in the 64-bit environment, we define NSInteger and NSUnteger to be long and unsigned long respectively. Otherwise, we define them to be int and unsigned int. And that's the only sort of conditional declaration we have.

So with this, then we go through the APIs where we want to make this change, and we change int to NSInteger, unsigned it to NSUInteger, and the NSArray methods now look like this. Instead of unsigned int, it's returning an NSUInteger, and instead of unsigned int and object index, it's taking an NSUInteger. And note that in the 32-bit world, this is still the same exact thing. It's an unsigned int.

So now the question is, what API should we do this in? We already mentioned data, we already mentioned array and string, and there are good reasons for doing it there. But what other APIs? Well, attributed string is very heavily based on string, and text storage is a subclass of attributed string. So if we're going to do string, we decide that we should really do attributed string. It would be weird to not have attributed string do this.

But if you do those, then you really need to do text view, because text view uses attributed string as its backing store, and it seems weird to have attributed strings capable of more than 4 gigabytes and text view not. NSRange and IndexSet are a type and a class which we use very frequently in all of these APIs to represent ranges and so on. And since we want to represent ranges in strings, text, etc., NSRange has to also be able to represent 64-bit quantities, and the same goes for IndexSet.

So we pull those in, too. TableView and OutlineView are often used with these other classes. The backing store of a TableView is often an array. TableViews represent ranges of elements with range and IndexSet, so I think these should come into the fold as well. Now, Matrix and Browser, they don't want to be left out, so they bring them into the fold. And, you know, after a while, this mental exercise says, "Let's just do everything." And let me talk about the benefits of why we'd want to do everything.

First of all, it gives us much better impedance match across APIs. You can take the return from any method, you know it's 64-bit capable, and pass it to any other method, and you know it's 64-bit capable. What this means, for instance, is you don't have to worry about truncation.

My array count could return a number greater than 4 gigabytes, but if table we did not accept a value greater than 4 gigabytes, every time you call this method or something like this, you'd have to worry, what do I do if it's not big enough? By making them both capable of 64-bit, the problem really becomes the rect of row method needs to worry about it.

If it itself cannot deal with more than 4 gigabytes internally, you know it will do something, but at least the APIs are consistent. This is also compatible with the CF index type and core foundation. As you know, we use toll-free bridging between foundation and core foundation classes, so data types go back and forth in various cases, and having NSInteger be compatible with CF index is quite important. And finally, you know, one big... One big reason is this whole consistency story.

Now, as API providers, us, or those of you who do APIs, you don't have to worry about, should this be 32-bit, should this be 64-bit? You know, the answer is clear, even if sometimes it seems like 64-bit is overkill, there's no decision. And as, you know, API consumers, when you're using an API, you don't have to guess whether it's 32- or 64-bit compatible. So it's the whole consistency story.

In addition to NSInteger and NSUInteger, we have one more change, and that concerns the float data type. Now both Quartz and Cocoa and other Cocoa-based APIs in Mac OS X use the float data type for specifying graphical quantities. This includes coordinates, sizes, color components, and so on. Now floats are great.

Floats are already a step above what some other systems did or we used to do with fixed numbers or integers and so on. They have a large range and they have good accuracy. However, sometimes it's not good enough. We do get bugs from developers who say, I have a view that's 10 million pixels or 10 million points high, and when I scroll to the bottom, it's just not pixel perfect.

Or when I zoom in 800 times in my view, things are not, you know, weird things are happening with the drawing. And that's because floating point numbers start to fall apart at those limits. So what we decide to do is in 64-bit, instead of float, we're switching to the double in all these APIs.

So again, we have another CG float type introduced in Quartz and used in Cocoa and Carbon as well to represent these graphical quantities. And note that all dependent types like NSRect, NSSize, etc. are also in Cocoa. And we're also now in terms of this type. The interesting thing about this change, by the way, is it's not dictated by the LP64 change. It has nothing to do with 64-bit, but we're taking advantage of this binary incompatibility change to introduce this other change.

Okay, so let me talk about porting to 64-bit. Some source code changes aren't necessary to port. You know, you might take your application, you might try to build it, and it might build if you're lucky. However, changes are likely needed. First of all, there are a number of deprecated, unavailable APIs. You might have to get off those.

In addition, if you do not change your source code, and if you're using ints and floats, it's likely that your program will not be taking full advantage of 64-bit. So you really want to switch to NS integers, NSU integers, CG floats throughout your code to take advantage of 64-bit when you convert your application. The good thing is we provide the script to help you convert your code, and it's a TOPS script.

So some of you might be wondering, TOPS? Is this some new thing Apple has done? And the answer is no, it's been there about 15, 16 years, but, you know, it still feels pretty fresh. It's sort of a refactoring engine of sorts that works on C and Objective-C. And it basically, you give it a script, and you give it some source files, and it does its thing in place.

And now I want to show you how to run this on TextEdit. Can we switch to the demo machine, please? Okay, now as I said, it does work in place. So first thing you want to do is you want to take your sources and you want to make a copy of them. So here I have the Tiger sources for text edit. I'm going to go ahead and duplicate this.

There you go, text edit, tiger copy, and let's just call it new so it's clear what we're doing. Now, TOPS requires a terminal window. We have not yet integrated into any of the new refactoring things we have in Xcode. Now, I want to go into that folder. Let's just drag and drop here. And you want to run TOPS.

Now, one argument you can give TOPS is the semi-verbose argument, which means show me what you're doing. Then you specify this script file, and the script file in this case is developer, extras, 64-bit, Cocoa.TOPS. And then you give it the name of the files you want to run it on. We just want to run it on all our source files, H files and M files. So here is how you might run TOPS.

Go ahead and run it. And it runs through your files, and it's done. As you can see, it showed the progress. And text edit isn't very big, so it was pretty quick. We can hide terminal. Now, next thing I want to do is launch File Merge. And File Merge is the best way to look at what the TOPS script did. I'm going to open this File Merge window a bit. This allows me to drag the original sources here.

[Transcript missing]

Now, you're looking at this thinking, Windows count, and this need saving argument here is the number of documents that need saving. These are not quantities that are ever going to be more than 4 billion, ever. So this change seems unnecessary, and I'm going to undo it.

On the other hand, the truth is, these values were never going to be up to 32-bit anyway. We used an int in the 32-bit world, and truthfully, you could have used a short. You might have even used a char. But we went ahead and used int, because that's the natural thing for 32-bits.

And in 64-bit, the natural thing is to use long. So in cases like this, where you have local variables, arguments, go ahead and use the NSU integer. Don't fight it. Just accept it, and things will be fine. And really, in 64-bit world, these things are in registers, and they're plenty fast. So that's the one kind of change you'll see TOPS doing. Now, another kind of change is down here, this one. TOPS will drop these pound warnings in your code where it thinks it wants your help.

In this case, it dropped this, because it saw a call to string with format, and it wasn't sure how to handle the arguments. Now, note that there's a %d here, and the argument it corresponds to is need saving, which is an NSU integer. Now, %d refers to an int. You would need to do %ld for long, while need saving is going to be int in 32-bit and long in 64-bit. We don't have a % specifier for an NSU integer. So this is a case where you sort of have to think about what to do.

And the recommended solution that we do is just go ahead and use %ld. Oh, and to edit in FileMerge, you open up this spiffy lower panel here, which lets you customize the changes. So let's do that. Let me just scroll this up a bit. So I'm going to go change to %ld, and I'm going to go cast the need saving to a long. Now, some of you might be thinking, oh, casting, that's so distasteful.

So %d says pick a long argument, and here we're making sure this argument is long. So this is a perfectly safe, very localized change, and it's totally appropriate to do in this case. And so we go ahead and do it. And this works for both 32-bit. In the 32-bit, it acts just like before, and in 64-bit, it also works. We won't get any warnings. So you can go ahead and delete this warning. Now, I will not go through this file one by one. I'll just scroll quickly, and I want to show you one more case.

So this is a case that's going to be sort of lame at the moment. It didn't need to put this warning because there was nothing to worry about, but it did anyway. In those cases, just go ahead and delete the warning. So I'm going to save this and close it.

Let me open another file to show you another case. This is multiple page view. It's a subclass of NSView. Note that the instance variable unsigned was changed to NSU integer. For instance variables, you should probably pause a bit and think about whether you really want it to be NSU integer.

If the value is not likely to go to above 4 billion, you can leave it as an integer. Instance variables and other data types, other values that go into core data, backing stores, your documents, etc., you might think about whether they need to be 64-bit because they are a little more persistent than stack variables and so on.

Now, in this case, we have very few instances of multiple page view in the application. There's only one per document, so I'm not going to worry about it. And in most cases, that's probably the right thing to do. But if you have object that's allocated by the thousands, you might want to think about how big the instance variables are getting.

So, with this, I'm not going to go through the rest of this. It's pretty straightforward. But I'm going to go open a version of TextEdit where I have fixed all the pound warnings and nothing else. So everything else is as TOPS did. So let's go ahead and open this version of TextEdit.

Let's make this window bigger. Now I'm going to go ahead and There we are. I'm going to go ahead and open the build inspector. And you can see that it's set to build I386. Let's go ahead and build this. And it's compiling and there it's done. If you can see down here, it compiled without any warnings. Let's go ahead and bring the inspector up again and change to 64-bit build and go ahead and build.

And you see that there's already a few warnings. Let's go take a look at what warnings we've gotten. The first one is about NSFont methods not being there. And again, this is due to the deprecated or unavailable APIs I mentioned earlier. Some deprecated APIs, we've totally removed them in 64-bit, and that's why you're getting this warning or this error when you're trying to build for 64-bit. So let's go look at this case here. Width of string.

Now you might be wondering how I deal with this. Command double click brings up NSFont.h, and you see that this is an NSFont deprecated category, and it says something about use string drawing API instead. Ah, that's good. With that, I will go ahead and delete these two lines which have errors in them, and I'll trust my demo assistant to do the right thing, and it did.

So as you can see, size with attributes is the replacement for width of string. And this is the more modern way to do this. So this is good. Let's save this, and let's go ahead and look at our other warning. There's this warning about glyph is encoded. This also is one of those deprecated methods. For that, we need to get rid of these lines and introduce two other lines. There we go.

And finally, we're getting a warning from QSort. Let's open that one. Now QSort is, as you know, a low-level function, and we're giving it a compare function. It's compare. It's complaining about the return type of this. If we search for this encoding compare, which is up here, you see that it was changed by the script to return an S integer. But QSort, being a low-level function, doesn't know about an S integer.

It really wants an int. So in this case, we can undo what the script did, change this back to an int. Now this is something you might have noticed when you were going through file merge, but of course I left it here so that we could enjoy this moment. So with that, I'm going to save this. I'm going to close it, and I'm going to go ahead and build.

So I'm going to open the app again, and it builds. No problem. So let's go ahead and run this. And there you go. This is a 64-bit text edit. I won't try to prove it right now to you, but you can do this yourselves and see that it's indeed 64-bit. Okay, if you can go back to the slides, please.

Okay, so the one question you might be thinking is, "Text-ed is 64-bit. Does that mean I should port my application to 64-bit?" And the answer to this is complicated. Frameworks, libraries, plugins. If you own a framework, if you own a library, if you own a plugin that other developers use, please go ahead and make it available in 64-bit so those other developers can make the decision to move to 64-bit themselves.

They need to have everything they depend on be 64-bit. For applications, it depends. Again, as I said, some applications really need it, others maybe not. Should they be ported in Leopard? You know, we don't know. By the way, the more detailed answer to that will be in the next session at 2 p.m. But it really depends on what you're trying to do. Just as an aside, the 64-bit Text-ed I showed you does have the added feature that it can actually open documents greater than 4 gigabytes, which is what the old Text-ed could not do.

And the Text system supports that. So it's added. It's actually capable. It already has some benefit that the 32-bit one didn't give you. But is that good enough reason to port to 64-bit? Again, it's really case by case. In any case, we encourage you to write 64-bit clean code, meaning for new code, use CGFloat, use an S-integer, use an SU-integer. Once in a while, build with 64-bit. Make sure you're on the right track so that if you do decide to port at some point, it's no big transition, no big porting work. And that would make it easy for you at that point.

More information about the conversion process and what it does to your code is in the 64-bit Transition Guide for Cocoa, which you can download from the WWDC Connect site. And we also have 64-bit labs, three of them, tomorrow, Thursday, and Friday, where we'll have engineers, many engineers available to help answer your questions. And there will also be machines if you want to come actually test out your 64-bit applications since you can't do it on your laptops. Okay. With that, I'm going to invite Mark Petrueli on stage to talk about Uniforce. on stage to talk about uniform type identifiers.

Thank you, Ali. So, back in Pantherd, we added a new facility to launch services called Uniform Type Identifiers, UTIs. And now in Leopard, we're adding full support to UTIs for UTIs throughout Cocoa. So, a really, really quick overview, if you're not familiar. What is a UTI? Well, Uniform Type Identification abstracts the concept of type.

So, if you've been working with Cocoa, you know, historically, we have a bunch of different namespaces that all really represent the same thing, you know, file types. There's file name extensions like RTF, there's HFS type codes like RTF space, and there's pasteboard type names, RTF keyboard type. The kind of strings that fly around in the NS Document API, rich text format, also sometimes used as the user visible form of the file format. And you don't see much of this in Cocoa, but there's MIME types to deal with, too.

What UTIs does for you is it provides a unified hierarchical identification system. So, you get to just work with this one type namespace throughout all the code in your application. And the goal there, the benefit is to reduce the complexity of your application. Another benefit, though, is the fact that we can build in information about a lot of different file formats right into the operating system.

It's in this core types bundle deep within the system. Like, if you're using a file type, you're going to have to have a lot of different file formats. It's in this core types bundle deep within the system. And you can see the names of the types that we support in launch services slash utcoretypes.h. If you're importing Cocoa.h, you're getting this header. Just a quick sample of what you'll find there.

Public.RTF, for instance, is the UTI, the uniform type identifier string for the rich text format. And this reveals right here a really big feature of UTIs, the fact that it's a hierarchical type namespace. Rich text format documents are plain text documents. I'm sorry, they're not plain text documents, they're text documents. And this is reflected by the fact that the UTI, public.RTF, is said to conform to public.text.

And so these are the ones, you know, there's a bunch of them declared right in Tiger and in Leopard. And, of course, you get to declare your own types that are identified by uniform type identifiers in your application's Info.plist. So what are we doing to support all this stuff? We're supporting Cocoa.h.

Now in Leopard, we're supporting them pretty much everywhere you would expect we would in your Info.plist declarations. We added a bunch of methods to this workspace. And this document controller, whose APIs deal very heavily in file types, now deal in UTIs. OpenPanel and SavePanel take UTIs. Pasteboard takes UTIs. And a miscellany of other AppKit classes also take UTIs or applicable. And, you know, one nice, another nice benefit of all this is there's now a better, match between Cocoa and other relatively new APIs in Leopard, like Spotlight's metadata framework and image IO framework.

So what kind of stuff will you find in your application's Info.plist if you're using UTIs? Well, just to go back, if you're writing an application that doesn't take advantage of any Leopard features, it looks something like what's on the top. This is like the document type declaration for text edit, for rich text format. And you have to put the file name extension, RTF. You have to specify a file name for the icon, a corresponding MIME type, a type name that will be used in NSDocument APIs.

And by the way, NSRTF pBoard type is actually an abbreviation that we allow you to put in your Info.plist for a much longer string that's more or less human readable. And then also the HFS type code, the four character code, RTF. So if you look at the one on the bottom now, if you're writing an application that just depends on Leopard, you don't have to declare all that stuff because in this case, RTF is actually declared as part of the system. So you can just say public.rtf.

Now we know we want you to use these new Leopard features, but of course we also know that you want to ship applications very often that still run on Tiger. In the case of this feature, we're really paying attention to what it will take for you to do that.

So it turns out it's pretty convenient that you can just leave all the Tiger stuff in your Info.plist and just add a little bit of stuff to start taking advantage of UTIs in your application. So LS Item Content Types in this example is ignored by Cocoa on Tiger. Launch Services still pays attention to it on Tiger. But because it's pretty much consistent with all that other stuff, you can't really tell that Launch Services is doing anything on Tiger. Except when it's being used from other applications, of course.

On Leopard, these things are ignored by Cocoa because there is an LS Item Content types entry and that's its trigger to start ignoring the old stuff. These ones, CFBundle type name and CFBundle icon file, are optional. They were required on Tigrip and now they're optional on Leopard. These are what you still provide even in an all-Leopard application if you want to override the default user visible name for a file format and its icon.

So, and this brings up an issue. I said that LS Item Content Types triggers a bunch of new behavior. Well, for backward binary compatibility, it takes more than that. You have to link your application against the version of Cocoa that's part of Leopard. So, this is something to pay attention to the first time you build your application on Leopard, is that if you have LS Item Content Types in your Info.plist, all of a sudden a whole bunch of new strings are going to start flying around in NS Document, Document Controller API, and some of your subclasses may need updating for that. So, and of course, if you just want to stick with the old behavior, well, that's what the SDKs are for. You can just link against the 10.4 SDK.

So, some new methods we added to NSWorkspace where we're putting most of the new support for UTIs that requires new methods. Type of file error given a path to a file, return its UTI or an error that you can present to the user explaining what went wrong while trying to do that.

Localized description for type. If you need to show the user the name of a file format, like an alert or something like that, this is the method to call. Preferred file name extension for type. If you're writing out a file and you need to know which file name extension to slap on the end of the name so that it gets identified properly as the correct type, this is the method to use for that.

File name extension is valid for type is actually something that's used by save panel. And why we introduced this is because in this hierarchical namespace now of UTIs, you really have to ask the exact correct question. You can't just get the file name extension for UTI and then compare it to the one that the user typed in. You sort of have to let Cocoa with launch services help answer the question. So save panel uses this when it's letting the user type a file name extension to see if it's one that works.

And the last one is pretty important. Type conforms to type, returns a yes or no answer. Answers the question, is this first type a subtype of the second one? So if you pass this public.rtf and public.text, it'll return yes. And this is what you're going to use in a lot of places instead of just comparing UTIs. You don't just compare them for string equality, you do this conformance check and it opens up a lot of flexibility in what your applications do.

So NS Document Controller has also been heavily updated. Methods like, there's a bunch of methods that have been updated, but I'll just go through a few of them. Default type in an application whose types are declared with UTIs will pick one of the document type declarations from the Info.plist.

And because LS Item Content Types is actually an array, it'll return the first UTI out of that array. So the order of the UTIs listed in that array matters for some things. Make Untitled Document of Type Error and all the other creation routines have all been updated to accept UTIs in addition to the old style NS Document type names that were used before.

Type for contents of URL returns the UTI for a file. And with the type that's returned, if you're checking to see can my application handle this, you don't do the equality check. You use that NSWorkspace type conforms to type method. And methods of AppKit's own, like NSDocumentController, DocumentClusterType, do do that.

They use type conformance so that if the type of a file is something that wasn't even known when your application was created, but does conform to one of the types that your application claims to handle, your application will be able to handle it, even though the file name extension is brand new, was invented after you shipped your application.

As long as, by the way, the application that declares that type, you know, declares the type conformance. Two methods that we're not updating, file extensions from type and type from file extension, don't work correctly with UTIs and we're not making them work with UTIs, we're just deprecating them because they answer questions that don't make so much sense anymore in the context of UTIs.

And likewise, NSDocument has been updated. So, readable types and writable types are default implementations with return UTIs. IsNativeType gets this conformance checking concept right. NSDocument has always been very customizable. It's a really heavily overridden class. We want to maintain that customizability as we add new facilities to it.

So, just to make sure everything stays open to you, we're adding this method, file name extension for type colon save operation colon. And it's used when NSDocument is going to save a file, but it doesn't know what file name extension to put on it yet because it's being done because of auto-saving or scripting or something like that. It'll call this, and if you have a complicated plug-in mechanism or something like that, you'll get a chance to control what NSDocument does.

So when your application is using UTIs because it has these LS item content types entries in its info P list, they're going to be flying around in all the method invocations between NSDocument and Document Controller. And if you're subclassing one of these, your subclasses are going to have to understand UTIs. So you're probably going to have to update them when you switch to UTIs.

And this is a good opportunity if you're updating what NSDocument methods your application overrides anyway, to stop overriding the ones we deprecated in Tiger, which by the way haven't been updated at all to handle this UTI stuff. So take the opportunity to get off the NSDeprecated methods and start using uniform type identifiers. And a great side benefit of this is much better error handling, which is why we introduced all those methods into NSDocument. in Tiger.

The open panel and the save panel have likewise been updated to accept UTIs. They had methods that would take arrays that were, we allowed you to mix file name extensions and HFS type codes together. Now you can mix UTIs in with all those, and we have a reliable way to distinguish them, so we can support this without having to add any new methods. Their implementations understand the rules that you just don't compare UTIs, you check one UTI for conformance with another. And likewise, save panel accepts and returns UTIs.

Pasteboard, of course, has been updated too. So, you know, it's going to be nice, it's going to be less code because there's just one unified namespace, one uniform unified namespace, for all the types that exist in your applications. There's not going to be pasteboard types and file name extensions anymore. So there's just UTIs. And now you can pass UTIs like KUT type TIFF, which is the Launch Services Declaration for public.TIFF, right into pasteboard methods.

Methods like NSPasteboard types for backward compatibility still return the old type names, so you don't have to update any code. They also now, mixed right into the list, return the new type names, the UTIs. And we've done this before, mixing the old and the new style type names in the arrays that we return, and your code is expected to pick out the ones it cares about, and that's worked out pretty well, and so we're doing it here too.

Now I know some of you have already tried to pass UTIs to NSPasteboard methods, and it didn't work. For instance, you promised data of a specific type using a UTI, and when NSPasteboard called back for you to fulfill that promise, the string was just not what you passed in. So now that we're officially supporting UTIs with NSPasteboard, if you pass in a UTI and you make a promise, you'll get that same UTI back when you're asked to fulfill the Pasteboard data promise. - Thanks.

So other AppKit classes. NSImage and NSImageRep have a pretty sophisticated system for determining what subclass of NSImageRep should be instantiated for a particular pasteboard item or file. And that's all being updated to take advantage of UTIs. And we're doing it in such a way that you don't have to update your entire application to use this new typing scheme at once.

So if you're overriding NSImageRep and it overrides the methods that return type names, you can start overriding the new methods that return UTIs instead. Without having to, for instance, update all your invocations of the NSImage methods that instantiate NSImages. And vice versa. So you don't have to do your whole application all at once.

And this is all, of course, documented in great detail in the release notes. NSSound and AppKit's additions to the distributed string have the same sort of methods for handling the types of things that get loaded. And likewise, they've been updated. So let me show you a quick demo of some of this stuff.

So let me show you just one of the methods that you might be overriding in an application like TextEdit. Updated to use UTIs now instead of the old style type names. This one is default type. TextEdit has a preference when you create a new document. You want to create a rich text document or a plain text document. And which one it does is control by this document controller default type, which it overrides just to get the value from the preference and pick one. UTI or the other.

No big whoop. Let me show you the InfoP list for what TextEdit looks like now. And by the way, TextEdit in Leopard has been updated to be an NSDocument based application. So when I show you TextEdit, as far as the user shouldn't see any difference we're hoping, but now you have more NSDocument example code.

So one of the nice things about just the way this is arranged in the InfoP list now is because you can list so many UTIs in one document type declaration, it ends up being, your InfoP list ends up being a great bit shorter and simpler than the old ones. So TextEdit handles seven document formats, including some Microsoft Word ones and Open Document, all in the same way.

So we just list them all. They all result in instantiation of the document class, and it's an editor for all of them. We break out rich text into its own entry because we have this little "Ls is Apple default for type" on there. So that's a little TextEdit trick to make sure that it gets to look at the rich text documents when the user is clicking on things.

So, and then the last thing, the other reason to have a separate document type declaration is because for a few types, um, TextEdit is just a viewer. So for this UTI, which identifies old style simple text documents, TextEdit can view them, but it doesn't try and save them. And for public data, this is one of the super types of pretty much everything. And, you know, can you really open any file in TextEdit? No. But TextEdit is meant to be a really general tool, so it's game. It claims it'll open public.data.

Now, just to show you something actually running, let me show you a little user interface refinement. You know how in open panels you're supposed to disable the files that the application can't actually open successfully? And we really weren't able to do that correctly in TextEdit before because we want to make sure that people who are using TextEdit as the editor of the last resort don't get hassled by the open panel saying, "No, TextEdit doesn't know how to to open it. So what we did is-- We're using just the regular Info P-List declaration from TextEdit.

We're saying that TextEdit can open any text document. So when you look at its open panel now, it can open any text document, including .h files, and TextEdit didn't have to list .h files explicitly in its Info.plist, but not icon files, because the icon file type is not declared to be a subtype of text or anything like that.

So, you know, this is a little bit better UI, but, you know, TextEdit as the editor of last resort, we don't want to, you know, hassle our users. They're like, you know, let me just open it. So we added this checkbox to open any type, and that makes, you know, the open panel, you know, enable this file. So, and TextEdit will give it a go. Garbage in, garbage out. So, so to do this UI, what we did is we overrode the NSOpenPanel delegate method that controls this stuff.

And if the preference is set to open file of any type, it returns yes. And if it doesn't, what it does is it invokes this NSWorkspace method that I keep telling you about. It gets the UTI of the file. And then one thing you have to do here, if it's a folder in an open panel, you have to return yes so that the open panel will let you navigate into that.

And we check to do that. We just see if whatever the file type is, if it conforms to the folder type, just return yes. And otherwise, well, let's see if it's readable according to what's declared in the application. So, and Texas Document Controller has a pretty simple method.

It's readable type. And it goes through all the types that all the subclasses of NSDocument has. There's just one, but it's general code, so it goes through the list. And it does stuff like if it conforms to the type, it says yes. So it's able to do the proper UI while still handling a wide variety of file formats that aren't even known at the time that TextEdit is shipping. So that's it for the demo. Slides.

So that's about it for UTIs. If you're writing a new application for Leopard, we want you to start using UTIs right away because it's going to simplify your code, your application is going to mesh well with all the new API that we and other groups keep introducing into Mac OS X.

And if your application needs to run on both Tiger and Leopard, we still want you to give it a try using UTIs. You'll have to pay attention to some issues, but you'll be able to get a few improved type handling and interoperability features, and you'll still be able to make your application work on Tiger while you do that. So for more information about UTIs, it's in the place it's been for a while now on the website. So developer.apple.com, Mac OS X, uniform type identifiers.

No spaces or underscores. And for information about what's in Cocoa, please, please read our release notes. This is one of the issues about which it is really detailed. So it's on the website, you know, release notes slash Cocoa slash appkit dot html. So with that, I'd like you to invite Kevin Perry on the stage to talk to you about resolution independence in Cocoa. Thank you.

As you can tell, I'm the intern in the AppKit group, and it's kind of obvious because I'm the only one actually wearing the T-shirt they gave us. But I will be talking to you about resolution independence. And what it means for you and your applications in Cocoa. So really quick, what is resolution independence? When you draw in resolution independence in your programs, there isn't really a direct relationship between the size of your drawing that you're drawing in your applications and the size it's actually going to be rendered to on the screen. And you've always drawn in units that are called points, which are resolution independent. And of course, screen coordinates are pixels. And with resolution independence, another way to define it is that one point will not map to a pre-known number of pixels on the screen.

So let's go over what exactly that means. And with Cocoa, we've always had a resolution independent model. As you can see here in this diagram, there are three different coordinate systems. First, on the screen, that of course is its own coordinate system represented in pixels. The window coordinate system, which we also call the base coordinate system, is also represented in pixels. And the view inside the window has its own coordinate system, which we also call the local coordinate system, which is represented in points.

And we can say that Cocoa has sort of a resolution independent model because on views, we're able to apply certain transformations to them, such as this view. We've just applied a 1.5x scale factor or zoom factor to it. And it's now no longer 450 by 600 points, but 300 by 400 points. And so there, we can kind of say there's not a direct mapping between points and pixels as there was before.

Until this point, Cocoa hasn't, in a general sense, taken advantage of this model for drawing its user interface elements. And you as developers, when you do your drawing in your applications, you could assume that you've got a one-to-one mapping of points to pixels and be generally safe. However, you can no longer make that assumption because of the changes we're making to resolution independence.

So to go over what problems we have with resolution independence, as technology advances and we have displays with higher pixel densities, we get additional quality, which is a good thing. However, take this example here. We've got a 72 dpi screen with a window that's 600 by 900 pixels, and it's drawn fine on the 72 dpi screen. However, without any modifications, we run that on a 144 dpi screen, and you can see that you basically have to squint to see anything in the view.

So this isn't a desired effect at all. And our solution for this is to have Cocoa automatically apply a scaling factor to the top level view and the entire view hierarchy inside your windows so that it will be scaled to a comfortable size for users. So in this example, whereas the window in the 72 DPI screen was 600 by 900 pixels, we apply a scale factor of 2.0. So we resize the window to be 1,200 by 1,800 pixels.

And you notice that the view is still inside 450 by 600 points. That's because points are resolution independent. And so we can scale the entire coordinate system without actually changing the coordinates within it. So just a quick overview. Remember that you've got screen frame units in pixels, window frame units also in pixels, and view frame and balance units in resolution independent points.

So when you do computations of coordinates for custom layout or whatnot, you need to make sure that you're in the correct coordinate system. NSView has always provided API for you to convert between coordinate systems and different views and also to the window space. You can see here that we're converting a rectangle from view nil, which means that we're converting the rectangle from the window space, that's what the nil means, to the local space of the view. And here we're doing the opposite. We're converting the rectangle to the window space by passing nil as the second parameter there. And of course there is a similar API for points and sizes.

Something you have been able to do before the advent of resolution independence is the following, where you want to compute the content rect for your window, and so you get the window frame, and just do a simple subtraction to find the rectangle that's for your content view. However, that obviously will not work when you've got a scale factor that's not 1.0, because you're going between coordinate systems there.

And so the correct thing to do is use the NSWindow API that's existed since 10.0 to do this for you. You just get the window frame and then pass it to content rect for frame rect, and that will automatically apply the, take into account the scale factor for you.

And in Cocoa, it's always been optimal, of course, to draw on pixel boundaries. And that was easy when you could assume that one point was equal to one pixel because you could just, in a local coordinate system, align things to points. However, if you have, for example, a non-integral scale factor now with resolution dependence, and you integralize all your point boundaries, then when it's converted up to the screen, you're going to have non-integral pixel boundaries, which is going to cause certain effects in your drawing that are not desirable. So the correct thing to do is not use, for example, nsintegral rect, floor, round, whatever on point values.

In the local coordinate system. Use the API that I just talked about to convert to window coordinates first. So here we've got a rectangle that we're first converting to window space. Then we can use nsintegral rect, and then convert back to view space so that we can draw it in our view.

And there is a method on NSVUE called center scan rect, which we intend to handle the basic case for you, the most common case. And the method works somewhat in the seed that you've got right now. It's not perfect, but we do intend to improve it for Leopard release. But you can go ahead and experiment with it now. This will do the conversion between coordinate systems and all that for you. All right, so let me see who is really proud of all their artwork in their Mac OS X applications.

OK, I'm sure there are a lot of you who like to boast about it. And it's really great because that's part of the Mac OS X user experience. And we really appreciate all the effort you do to make those good looking images. But as I said before, with the scale factor applied to the entire view hierarchy, we're also resizing all of your images automatically.

So just imagine all of your beautiful artwork scaled up to, say, twice its normal size. Probably not a pretty picture in most cases. And so this is something that Cocoa can't solve directly for you, but we do make it pretty easy for you as developers, though it may not be quite so easy for your graphic designers.

So, there's two possible solutions. Where possible, try to switch to vector-based art. That is obviously automatically going to scale correctly and look great at any scale factor. However, for bitmap images, if you need to use those, we suggest that you supply your bitmaps at one time and four times their normal resolution in a multi-representation TIFF.

Making sure to set the DPI appropriately so that they are actually drawn to the same size. And you can see an introspective view of one of these images where we've got two image representations with the same size but The first step is to create a new image. The image is going to be four times the size for the second image representation.

When you do that, when you ask NS Image to draw your image, it will automatically look at the scale factor and select the correct image representation. Using the quality image interpolation that we have, scale that down to the correct size and make sure that your images still look great at any scale factor.

And as Chris Parker mentioned in the previous talk, we do intend to publish several named images that will use high res art for the most commonly used images, such as the ad. and remove plus and minus signs. That's not in the seed at this point, but we do intend to put that into the leopard release.

OK, so we're going to have a quick Q&A, but this is going to be kind of different because I'm going to be asking all the questions, and you get to listen to all the answers. So you can wait until later to ask all your questions. So the first question is, how do I know what scale factor has been applied to my window? The answer is, well, you really shouldn't need to know this. But NSWindow does provide API for this in the user space scale factor method. And this might be useful in cases where you want to maybe save your window frames to disk manually.

So before you do this, you want to invert the scale factor done to the window so that in case the window is read from disk in an environment with a different scale factor, it can be reapplied when you read it. And you don't get any explosions of window size or anything. But remember, this is not for use in converting between view and window coordinate systems. There are a lot of other transformations that may be applied to your views that you just can't take into account with this.

So use the API that we've provided in NSView. It's really the simplest way to do this. Next question is, I have an offscreen view that shouldn't be scaled. How do I prevent automatic scaling? Well, in Tiger, we've provided this ability for you using the NSUnscaledWindowMask when applied to the NSBorderlessWindowMask. Yes, that's right. You can only use this on borderless windows. But it's a pretty simple solution here. You just apply it. and pass it into the init method.

Okay, next question. I happen to know that OpenGL has to draw in pixels for it to be correct. How does that work in a scaled window? The answer is we're going to make NSOpenGLView internally perform an inverse transform on the bounds of the view so that internally the OpenGLView can draw directly to pixel values. And externally, you're using the view.

It'll be normal. The frame is still in points. So, all this is handled automatically for you. You don't really need to worry about this. But discuss that because for those of you who have resolution dependent views, you can do the same sort of transformation, though this shouldn't be very common.

Last question, probably one of the most important. How do I test my application for resolution independence? We hope you all go and do this fairly quickly, because it's fairly easy. You just launch Quartz Debug, and you can find this window, the User Interface Resolution window. And we suggest choosing 1.25, 1.5, and 2.0 as representative scale factors to see how your app will work in general with resolution independence. And then relaunch your application and see how it works. So it's that simple. OK, continuing with the common theme here, we're going to-- do resolution independence check for text edit. So let's get the demo machine, please.

Thank you. So let's go ahead and set, let's say, just 2.0 scale factor. We're going to test this out. Launch text edit. Boom. That actually looks pretty good. We haven't done any changes to this at all. We've got pretty much just standard Cocoa controls and Cocoa text. So there's really not any work that needs to be done to prepare a text edit for resolution independence. So no work done there. We really like that.

For a more representative view of this, we've prepared a little bit of sample code here. We've got a beginning of a Minesweeper game that uses custom layout with images and so forth to draw its minefield. So here we've got a typical minefield. You can resize it or make the minefield larger, whatever. Just really simple. But let's go ahead and run this at, say, 1.25 scale factor.

I can't really see that there on the projector very well, but I've launched Pixie here to show you. In between a lot of these images are these pixel-wide cracks. And as I mentioned before, this happens when we're drawing on non-integral pixel values. So there's something wrong there with our drawing routines. And also when we try and resize this minefield, the window isn't getting set to the correct size. So let's go and investigate what's going on there. Go ahead and open our View subclass here.

Here we've got our drawRects, and we're calculating the size of the rectangle here that we're going to draw the image into. And here we're calling a method which is just right up here. This is where we're integralizing the rect and everything. And you know what, we're doing this in view coordinates. We haven't done any sort of transformation here. So let's go ahead and insert. The necessary method to convert first to window coordinates.

And then we can integrate this directly in window coordinates. And then we convert back to view coordinates, because that's what we have to draw in. So that should fix the pixel crack problem. And down here, where we set the number of squares, so this is where we're resizing our window as well, right here, we are computing the delta that we want to resize our window by. And you know what? We're doing that in terms of view coordinates. So that isn't being set correctly with our scale factor.

So we want to make sure that we convert the size first to window coordinates so that the window is resized correctly. And there's a little interesting behavior of the convert size to view method. If you just happen to have negative values in your size, when you pass it through convert size to view, it's normalized to a positive value. So we actually have to, before we convert it, We need to check whether the size is negative. Save that state.

And then, after we're done converting it, we restore it. That should handle the window resizing, and we'll just run this again. Again, we're still in 1.25 scale factor. And switch back over to Pixie here, and you see there's not any pixel corrects anymore. We've solved that problem by making sure that we're drawing on pixel boundaries. And when we resize the minefield, it resizes it to the correct size of the window.

So that looks pretty good for preparing this app for resolution independence as well. So it could be that simple for your applications as well. So let's switch over back to the slides if we could. And let me invite Kristin Forster back up for a summary of all the points that we've gone over in this talk.

Thank you, Kevin. So to summarize, we'd like you to consider converting your application to 64-bit for increased data handling capabilities. And as Ali said, this is especially important if you have a plug-in or a framework or a library that somebody else is relying on. We'd like you to start using uniform type identifiers for improved type management, and we'd like you to prepare your application for resolution independence so it looks great on high DPI displays.

For more information you can contact Derek Horne, Application Frameworks Evangelist. You should also check out the documentation attached to this session on the conference website. And please come see us in the APCIT lab Thursday at 2:00. And as Ali mentioned, there's also 64-bit labs running throughout the week.