What’s New in AppKit for macOS

Session 210 WWDC 2019

Learn about the latest APIs in AppKit and associated frameworks. Get an overview of the enhancements coming in macOS Catalina to help you save time, take advantage of the latest hardware, and add polish to your application.

[ Music ]

[ Applause ]

Hello and good afternoon.

I'm Chris Dreessen.

I work on the Cocoa Frameworks.

And we're going to be talking about what's new in AppKit this year.

So, let's dive in.

We have a lot of functionality.

We have some additions to NSColor, some additions to NSScreen.

We have a whole grab bag of features to help you with text and fonts.

We're going to do a little pub crawl right here in this session.

We're going to talk about toolbar and Touch Bar and sidebar.

And then we're going to talk about some new control functionality in AppKit and macOS 10.15.

And we'll wrap up our section on AppKit by talking about NSEvent.

We'll take a small jump and go into some foundation features including some geometry enhancements and new formatters to make localization easier than ever.

And then we'll wrap things up with some notes on system extensions in macOS 10.15.

So, before we do that, though, let's go ahead and address the big thing.

In macOS 10.15, AppKit is not the only UI framework on the system.

We have two new UI frameworks joining us.

So, one of these you've probably already heard of and that's UIKit.

In macOS 10.15, you can recompile your apps and run your iPad apps on the Mac.

And if you have a well-maintained AppKit app, this is probably not going to be very interesting for you.

But, if you have an iPad app you've never brought to the Mac or you've had an iPad app you've been investing a lot in the last decade, not so much the Mac, this could be very neat.

You will still use AppKit to add the finishing touches to your UIKit app for things like toolbar and Touch Bar support.

But we think that can be very interesting.

The other new framework and we're really excited about this is SwiftUI.

And SwiftUI is this neat declarative way of describing your user interfaces and the transitions between them, ways of binding them to your model data.

And this we think is a great way for making UIs.

It's Swift native and supports all sorts of Swift language features that we really like.

Again, this is something you will use in conjunction with AppKit.

You will embed it in view hierarchies potentially all over the place.

So, there are sessions about of these frameworks all throughout the week and we think you'll enjoy them.

On to AppKit.

So, in macOS 10.15, we have some new system colors in NSColor.

We have teal and indigo.

These are system colors which means they're actually dynamic.

Depending on which appearance you use they can look differently.

But we think these are great colors and you'll enjoy using them in your apps.

We also have an under the hood change in NSColor we want to talk about.

And that's that NSColor is now using the tagged pointer system we also use for NSNumber and NSString.

And what is a tagged pointer?

Well, normally for NSColor, we would store the various component values or other information as eye bars in an allocated object.

And with tagged pointers, we instead condense that information down to a smaller number of bits and store in the pointer itself.

So, there's no separate allocation.

If you had code that was very heavily allocating or deallocating NSColors, you're going to notice a performance one from this, so we're excited here.

Generally, that's going to be an invisible change for you but there is a case I want to point out.

If you're accessing a derived property of the color, for example the CGColor, with the non-tagged version, that could be cached as an eye bar in the color and it's possible the CGColor would live as long as the NSColor.

In the snippet we have on screen, you could see that we passed that CGColor to a ContextSetFillColorWithColor, and that would have worked OK in the past.

With tagged pointers, using it outside the autorelease pool is potentially hazardous.

So, it'd be a good hygiene not to do that anyway but something to be aware of just in case.

So, a lot of us have used color panel.

And there is a near and dear to us eyedropper tool in the color panel that lets you click it and bring up a magnifier like this and then select a specific color on screen to read the RGB values of.

In macOS 10.15, we're exposing this functionality to you too with a new class called NSColorSampler.

So, NSColorSampler is very simple.

It has a single class method on it called Show.

You pass a block to show and we will call you back with the selected color or nil if the user canceled out of this.

And we're really excited to share our implementation with you too.

If you have your own implementation something to be aware of in macOS 10.15 is that Screen Recording is going to prompt for user permission.

And this is something that doesn't happen if you use NSColorSampler itself.

There's another NSColor API that we find incredibly useful.

We call it the Dynamic Color Provider.

And it's really just a new initializer on NSColor.

It takes two arguments.

One of these arguments is a block and that block accepts an NSAppearance.

When we go to resolve the color either because we're drawing it in a bitmap context or writing it to a file or using it as a layer background color, we're going to call this block with whatever appearance is appropriate at the time.

In our snippet on screen, we use the NSAppearance bestMatch method to tell us whether the requested appearance was more like aqua or more like darkAqua.

And we have hard-coded colors for each of those cases.

This can be very useful where you want to have some programatic decisions and dynamism in your use of NSColor in your application and this is much simpler than trying to propagate a hard-coded literal color across a view hierarchy on appearance change.

So I mentioned there were two arguments.

The other argument is a name.

And the name is very significant for coding purposes.

If you send one of these colors over an NSConnection an NSXPCConnection, when we decode it we're going to look it up by name.

So it's also important to register that color on both sides of the connection.

So colors are great.

NSColor is great.

And they're all better with screens to view them on.

So we'll talk about some NSScreen improvements.

If you're ever implemented a screen picker control, you may have wanted to have a user-facing string to identify an NSScreen with.

And if you use the inherited description method from NSObject, your users probably were not content with NSScreen 0x6000261e460.

So, in macOS 10.15, NSScreen.localizedName will give you a nice human readable string to describe that screen.

All right, talking about screens more generally.

Over the last many years, screens have been getting brighter and brighter and brighter.

And there are very few people who run their screens at max brightness anymore.

And if we apply this to a specific picture, we can imagine that black is a zero component value and one is the white component value.

And as we dim that screen, we're sort of compressing what one means in absolute terms.

We are emitting fewer photons per unit value in our software.

But that monitor hasn't lost that ability to produce bright colors.

We can instead allow Extended Dynamic Range content, content that isn't clamped in the zero to one range and keep using the maximum brightness potential of that monitor to make some images where we have things brighter than our reference white point.

And this isn't actually a new feature.

This is something you've been able to do on a number of Macs since macOS 10.11.

There's two APIs that would help you do this.

One of them is NSScreen.maximumExtended DynamicRangeColorComponentValue.

It actually does stuff.

It's not just the longest Objective-C property name you've ever seen.

And what we would do is when your system was in the Extended Dynamic Range mode, it would go ahead and tell you the maximum component value you could use before clipping would happen, so that could be 1.3 for example.

If the system wasn't in this mode, it would just return 1.0.

So you put the system in this mode using a different API and that is CAMetalLayer.wants ExtendedDynamicRangeContent.

There is also an API NSOpenGLView called wantsExtendedDynamic RangeOpenGLSurface.

And when either of those is set to true, you can get that extra headroom from this API.

So something we've added in macOS 10.15 isn't even longer Objective-C property name.

The important bit is actually shorter.

That's the maximum potential piece.

And this will tell you that headroom, that maximum value, even if the system isn't in the extended dynamic range mode, and that can be very useful if you are working with extended dynamic range content, you might have multiple possible pipelines you can send your image data through.

And a floating-point pipeline for extended content is probably more expensive than a BGRA pipeline, for example.

So knowing that you have that headroom to play with ahead of time let you conditionalize that support.

So there's another API I want to talk about specifically for the new Apple display we saw yesterday.

And that display is absolutely amazing because of how bright it can get.

It can sustain a thousand nits indefinitely.

And because of that ability, we like to say it's a reference quality display.

We can say that for a specific component value, there were will be an absolute number of nits for that pixel.

Now, it's very bright but it's not infinitely bright.

If you go ahead and produce a bright enough pixel value, it won't actually be able to represent it and that reference quality attribute we talked about will be compromised.

We'll have to scale the image content down to fit in the brightness range available.

And that's what the NSScreen maximum reference value is telling you.

If you exceed this value, you're not going to be able to dish out absolute nits anymore.

OK. So I mentioned CAMetalLayer as part of a way of getting extended dynamic range content.

And I want to point out something that if you're using MetalLayer on the Mac today, you should be doing, and that you should be paying attention to which screen and which metal device you're going to see MetalLayer is going to be displayed on.

That's a little convoluted.

You have to get your window and your window screen and your screen's device description.

And then you have to ask it for this hard-coded string that you found on our documentation but we never exported in any of our headers.

And finally you'll pass that CD direct display to figure out which device to use.

And it's worth it because that will allow you to avoid moving the data between GPUs across the system bus but we've made this better in macOS 10.15.

CAMetalLayer and MetalView have a new preferred device property that will answer that question for you.

It really reduces your code around this.

So we're excited that you get to use that this year.

And that covers what we wanted to say about screens.

And now we're going to go into a whole diversity of text features.

The first of these I want to mention is NSTextView uses adaptive color mapping for dark appearance.

And this is a feature that you can turn on and off and text at it.

I have snapshots of them in both the aqua and darkAqua appearances using colors from the system crown crayon picker.

And you can see that when this feature is activated, snow and licorice for example almost invert colors.

Meanwhile the more saturated and vibrant colors retain that same color character but changed brightness to better match the appearance therein.

So this can be very useful for making text use with plain content where you want them to fit in with the current appearance.

And it can be very useful for each text documents where the desire to make them fit it is more important than making them appear exactly as they will on a printed page for example.

Generally, we think this is the right thing to do but if you have an app that's focused around authoring rich text content, you probably want to either opt out or offer the user the ability to turn this off so they can decide that they want a representation matching printed content more than their screen.

So I think most people in this room are familiar with NSSpellChecker if not as a developer then as a user.

And in macOS 10.15, we're happy to announce that NSSpellChecker has a successor.

And that successor is NSTextCheckingController.

So, NSSpellChecker works with NSTextView but NSTextCheckingController works for the variety of systems we use in UIKit and WebKit and AppKit.

And you can use it yourself by implementing the NSText checking client protocol.

But in addition to spell checking it does other things.

It does grammar checking.

It can do data detection to find URLs and phone numbers and dates.

And you can control all of that and configure whether it's going to a merely highlight misspellings or automatically correct them.

So we think it's a very flexible API and if you're big on text input, it's something worth checking out.

The small edition related to text is that almost all of the NSText related classes support secure coding now.

So if you wanted to use these as part of a vocabulary over NSText PC connection, they will work splendidly.

We're probably all familiar with NSFont and slightly few of us are familiar with NSFontDescriptor.

NSFontDescriptor is a neat class that lets us look up and transform fonts using sort of semantic attributes about them.

So I made a little sample up here.

And the top row is a font I've hard-coded in Interface Builder.

But the subsequent rows I've configured using the NSFontDescriptor system design API which is new in macOS 10.15.

And lets me go ahead and say, I want to switch to a rounder font or a Serif version or a monospaced font.

And there's a session about this which we'll go on to much greater detail about it but we think this is a great way of stylizing your applications.

So, if anyone has a cross-platform app where they are writing rich text documents on both macOS and iOS or even in app that has not cross-platform where they are reading documents from the other platform, you may have hit a situation where even though the document is specifying the same font sizes, they visually look very different.

And that's because we have very different screen densities between our iOS devices and Mac devices.

In macOS 10.15, we have new attributed string APIs that you can use when reading and writing attributed strings to rich text documents.

And you can tell us what the source and destination operating systems are and we will automatically adjust font sizes to make them appear visually the same.

In this case we brought our 160 point font down to 120 point which is roughly where they're visually the same height.

The last text feature I want to talk about is an addition to NSLayoutManager called usesDefaultHyphenation.

And in the past, you'd be able to get the NSText Class that's hyphenate for you by using an NSParagraph style to control the threshold of hyphenation.

This layout manager property is a little bit easier to use.

It defaults to off, in which case you get no hyphenation like on the left.

And if you set in on it we'll pick up a default value for hyphenation.

And you can see on the right that we've hyphenated encyclopedia and internationalization.

So we think these text APIs will really help you polish the corners and edges around your text systems.

We have some great new stuff in NSToolbar.

The first edition is a new property on NSToolbarItem called isBordered.

In the past, if you wanted to get this push button style appearance in an NSToolbar, you would have to allocate your own NS or instance of NS button and configure it and then you'd use it as a custom view on your toolbar item.

And that would work but it was extra effort for you and additionally you couldn't take advantage of NSToolbarItems built in support for automatic enabling and disabling.

But if you go ahead and use this isBordered property, you'll get all of that for free.

Additionally, NSToolbarItem has a new title property which lets you configure your toolbar items to be a string-based buttons instead of iconic ones.

And this is distinct from the label property which would appear below these buttons if we had configured the toolbar as your labels as well.

And that is not the NSToolbar related class to gain functionality in macOS 10.15.

We have also added a number of features to NSToolbarItem group to make it incredibly versatile and useful.

And the first of these is a number of convenience constructors for it, and these are just going to let you build the same great toolbar UIs in fewer lines of code.

But NSToolbarItem group has become more flexible at the same time.

If you look at the image on the right side of the screen, you can see that it now has support for representing its items as a segmented control and it can also represent them as pull-down and pop-up menus too.

And so this makes it a very versatile toolbar control because of the way is uses the other toolbar items as vocabulary, we can also automatically create a collapsed representation for when you have too many toolbar items for the window widths.

There is a very similar effect you can get using a new NSMenuToolbarItem class.

And this is much like the menu item representations of the toolbar item group but it uses an NSMenu as currency instead.

And that's significant because you can use NSMenu features like submenus or separator items or even the ability to use a custom view for your menu items.

So it's a very powerful tool if you want to have sophisticated drill-down behaviors in your toolbar.

NSTouchBar has also received a few enhancements in macOS 10.15.

The first of these I want to note is a new class property on NSTouchBar itself and that's isAutomaticCustomize TouchBarMenuItemEndabled.

And that will sound familiar NS application has that exact same property.

You could use this Touch Bar one in a code where you want to defactor in such a way to not reference the NSApplication.

And one example of that is if you're writing UIKit application, you're not going to have an NSApplication instance to talk to anyway.

A more significant control is the new NSStepper Touch Bar Item class.

Just like an onscreen stepper, this is very useful for when you're doing a discrete entry for things such as dates and numbers but given the larger size in the Touch Bar and the horizontal layout, you can also use this for new applications like visually selecting the tool on a drawing app.

So we're really excited about this new Touch Bar Item class.

We think you'll be able to put it to good use in your applications.

NSSlider Touch Bar Item has a few small enhancements too.

You may have experienced a case were used an NSSlider Touch Bar Item and it was a little bit too small for your taste.

You could fix this by grabbing the slider from the Touch Bar Item instance and adding auto layout constraints to it.

In macOS 10.15, you can just set the minimum slider width directly on the item and we'll take care of that for you.

Similarly, if your slider has global ambitions, you can now check them by using the maximum slider width property.

And lastly, we'll talk a little bit about sidebar metrics.

In macOS 10.15 you can toggle the size of a sidebar using the setting and the general pane of system preferences.

And we default to medium but you can also use small and large.

And I want to call this out because if you haven't known about this feature you maybe have not been including artwork appropriate for the small and large sizes.

So if you're using bitmap artwork, go ahead and have representations for that or use a resolution independent version.

Similarly, there's a new feature in macOS 10.15 to allow automatically picking the light and the dark appearance, that's also available in the general [inaudible].

OK. We covered controls for toolbars and Touch Bars.

Let's talk about some controls that are more broadly applicable.

The first of these, we're excited to announce this is NSSwitch.

And NSSwitch is a full subclass of NSControl.

It supports bindings, it supports formatters, but it exists alongside the existing check box functionality.

A question you probably have in your mind is when should I use NSSwitch and when should I use a checkbox?

And if you are already using a checkbox, you should probably keep using a checkbox.

They're generally the right control to use.

We think NSSwitches are better used when you have a really heavy toggle, something that's toggling a lot of functionality on and off.

And in this example I have here, we have a master toggle on the left which is going to enable all of the individual sharing services on the right.

In macOS 10.15, we've invested a lot of effort in NSCollectionView.

And one of the things we're excited to talk about is compositional layout.

And in this scheme, you no longer need to subclass NSCollectionView layout to modify it.

There are number of interesting features you can throw in, such as container-relative sizing, layout breaks and sections as well as nestable groups, and also making individual sections scrollable.

And of course, all of that works in both right to left and left to right languages.

There's also a new collection view feature called diffable data sources.

And these are identifier-based data sources that let us track things like the addition or removal of items or the movement of an item between two locations and we can automatically infer the right animations to use.

So this means you won't have to use performBatchUpdates or reloadData.

OK, backing away from specific controls to an IB feature.

IB storyboards are a really great way of connecting the various view controllers in your application.

And historically, your view controller would be initialized through in it with coder and you'd have to find a different way of getting different information into the view controller.

That wasn't necessarily difficult but it did mean your code was factored maybe more broadly than you wanted.

If you look at the snippet here you can I have a function called showPetDetails and this returns a new view controller passing along a coder from IB as well as inserting our own notion of a selected pet name.

So this is a great way of bundling those separate initialization and configuration steps.

And the other important thing to note about this is there's this IBSegueAction annotation attached to this function.

And by doing that, we can go to the connections inspector and interface builder for our segue and wire it up directly to this method.

So we think this is very useful for adding extra configuration into your view controllers and storyboards.

So, a great feature of all AppKit controls is how they support auto layout.

They automatically know how to measure their content and feed that into auto layout engine.

And that means that you can do things like change the strings on these text fields and buttons and auto layout would just automatically reflow view hierarchy and window for us to look great.

Sometimes, though, there are UIs where that's actually not important.

So in this grid view, it's the grid that is determining the size of everything else and the intrinsic size of these labels doesn't actually matter.

But normally, AppKit would still go ahead and measure these controls and feed them into the auto layout engine even though they won't affect the final result.

In macOS 10.15, you can turn that off.

NSView gains two new properties to turn off the measurement behaviors for both horizontal and vertical axis.

And we think that can be a great way of getting back some performance in UIs where you know the intrinsic size of the control isn't going to matter.

There's one other controller related thing I want to talk about and it's a bit more broad than NSControl.

That's NSResponder and its behavior relating to block capture.

So if you look at our snippet on screen, you can see we have two blocks.

We have an outer block that's going to be executed on the background thread and we have an inner block that's going to be executed on the main thread.

And this is a fairly common pattern.

We do our work in the background and then we go ahead and assign the results of that work, to the UI on the main thread.

But this snippet in macOS 10.14 could have led to crashes and the corruption.

And that's because if the only thing retaining this field is these blocks, the order of destruction of the blocks isn't defined.

If the outer block, deallocates last, the text field will deallocate on a background thread and the text field by virtue of being a responder is part of the responder chain.

It can be part of the view hierarchy or the key view loop, so there's all these global data structures that aren't safe to modify from the background which is what dealloc would do.

In macOS 10.15, this isn't a problem.

NSResponder will automatically move the dealloc method for it and its descendants to the main thread.

So it becomes safe to capture these in blocks without being concerned about where those blocks are released.

And we think this is going to remove a whole complex set of crashes from many of your apps where the corruption from doing work on a background thread isn't visible until seconds or minutes afterward.

So, I want to talk about open and safe panels for a moment.

We have a fairly broad change in macOS 10.15.

And that's that open and safe panels are now using a separate process.

This is a behavior that's already been present for sandboxed applications and we've simply brought it over to the remaining apps.

Generally, this should be a silent change.

We don't think you're going to notice anything here apart from some performance and security benefits.

However, if you were sub-classing open and safe panel and relying on specific view hierarchy configurations, you might run into problems and we'd be happy to see you in the labs to talk about those and help work through them.

We also have some new NSWorspace methods in macOS 10.15.

These are methods that can open one or more URLs or open an application.

And that will sound familiar because you'll think NSWorspace already has methods to do that.

It does but the new methods are asynchronous.

So they're not going to block the main thread at all.

When the application finishes launching or the operation is canceled, we will call you back using a completion handler.

And in addition to be an asynchronous, these methods also offer a high degree of control over how we launch those URLs and applications.

And that control is achieved through the new NSWorspace open configuration object.

This is just a sampling of the knobs it has to play with but you can control whether the user needs to participate on this process.

So for example, if you're requesting a server mount or if we don't know what application to use, the user might need to pick which application and you can instead suppress that behavior and just cancel the open process.

You can also control whether an application or document is added to the Recents menu.

And there's an entire variety of things for controlling which applications can be hidden on launch or which are activated in the background or foreground.

So we think whatever your URL application opening needs, NSWorspace will accommodate it wonderfully in macOS 10.15.

We're going to talk events for a little bit.

And there's a feature I want to show you.

And this is if you hover your mouse over the green button in the window title bar, you're going to get this new menu.

And the first set of options concerns things like making a window full screen or positioning it in a shared full screen space.

And those are fairly useful.

But there's another feature that I think is even more useful and that's the ability to move a window to another display.

As someone who is constantly moving their laptop to different external monitors, I found this to be incredibly useful but you don't actually have to use a conventional display, iPads will function as additional displays now too.

So you kind of see where I'm going with this given that the iPad application is named pencil draw and we have this beautiful cursive script on there.

This supports Apple Pencil so many iPads can now function as tablet devices for Macs.

So in macOS 10.15, we're going to have many more tablet users.

If you've been thinking about adding tablet support to your app at any point in the last many years, now is a great time to adopt it.

So I'm going to show what you need to know to do that.

Tablet events come in basically as normal mouse events.

There's a difference though and that's if you pay attention to the NSEvents subtype field, there is a tablet point value there.

And when you see a tablet point event it, one, comes from a tablet but it's also going to have pressure information attached.

And paying attention to that pressure information is critical for making things like that nice cursive stroke where you have different pen width throughout it.

If you've used the pencil APIs on iOS in the past, there was something that you could do by registering a handler to retroactively receive updates to pressure in the past.

That is not present on the Mac.

You can just pay attention to the pressure field in the NSEvent.

There's another convention related to the Apple Pencil I want to mention.

And this is that you can double tap it the side of the pencil and it will switch whatever the current tool is in the drawing app for example.

And we call this the change mode event.

There's a new event type for it on this event and there's a new responder method to handle directing that through the responder chain.

In many cases, you might want to have tablet functionality that isn't actually anchored in a responder subclass like a view but you might still want to handle events directly and there's a way you can accomplish this using the existing local event monitor API.

So if you look at the snippet on the bottom of the screen, you can see that we use the NSEvent add local event monitor for events function to catch these change mode events.

And then we just cycle through our tools and we return the event allowing it to flow through the rest of the responder chain.

So that can be a great way of factoring your code more usefully.

OK. Let's talk about a foundation feature.

We have some new geometry data types.

I know normally when we get into foundation everyone is on the edge of their seat.

This year, you can pick which edge of the seat you're on [laughter].

The data types are NSDirectionalRectEdge, NSDirectionalEdgeInsets, and NSRectAlignment.

And instead of working in currency like min-X or max-X or left and right, these are using leading and trailing identifiers instead.

So they'll automatically flip based on context in the left to right or right to left system.

And NSCollectionVeiw uses these exactly for this purpose.

We think you'll be able to adopt them directly in your applications too.

Another foundation feature I want to talk about that will help with localization is new formatters.

The first of these is the NSRelativeDateFormatter.

And NSRelativeDateFormatter has two important properties, a dateTimeStyle which allows you to pick whether you're sort of working in absolute units or more colloquial terms, the one week ago versus last week, as well as the unitStyle controlling how verbose or how terse our languages.

There's also a new NSListFormatter class.

And NSListFormatter is interesting in that instead of formatting sort of a scalar object, it will format an array of objects instead.

And it will do that by using a separate item formatter for each object individually.

The value the ListFormatter adds is in knowing where to place the commas between the individual formatted strings as well as whether an Oxford comma is necessary or a conjunction.

There's a section covering this in much greater detail later, which I encourage you to check out.

Foundation also has a new feature we're very excited about called Combine.

And Combine is a Swift API for connecting properties of the objects in your applications to other properties in your application.

So a specific example I want to show is this awakeFromNib method we've implemented where we go ahead and we bind to the name property from our model object to the value of an NSTextField.

And whenever that name changes, that text field is going to update its string.

So this is incredibly powerful.

It has applications beyond just UI binding.

And there's another section dedicated to specifically this that we think is going to be fantastic.

So go ahead and check that out this week too.

And finally, I want to mention some changes in system extensions for Mac OS.

We have a small addition to the system extension family in the form of a non-UI file provider action extension.

And if you are familiar with UI base file or file provider action extension, this is the same thing but sometimes you just don't need that extra UI.

You can do without it and this satisfies that need.

Something else that we think is even more interesting is there's functionality that in the past you would have had to use a kernel extension to add at the OS.

And we've now made system extensions to do similar things.

We have new network extensions.

We have DriverKit to help writing certain types of device drivers.

And we have a new endpoint security system that will help write antivirus software.

So if you have been writing kernel extension all these years, we think these will be very useful for you.

We're excited with a security enhancements we get to make around this and we think you'll be excited about not doing kernel-mode debugging anymore.

So, that covers our additions.

I want to remind you of many of the great things that we covered today.

We had additions for NSColor including new dynamic system colors, new ways of embedding your own programmatic dynamism, under the NSColor system as well as the ColorSampler class you can use yourself for picking colors directly from the screen.

We covered a number of APIs and NSScreen including those for you to make a great usage of extended dynamic range.

And we have an entire variety of text enhancements.

If you want your apps to look great in Dark Mode and you're presenting either simple or rich text, the Dark Mode enhancements are going to great, the text tracking controllers will let you enrich your own text engines like never before, and our compatibility between iOS and Mac OS regarding text sizing is very useful especially given the presence of UIKit on the Mac this year.

We covered controls like NSSwitch as well as collection view.

And we definitely encourage you to check out the collection view section because collection view has become a very important part of our UI vocabulary.

We covered some great enhancement for NSToolbar allowing you to make push buttons more easily and more versatile ways of using the tool bar item group.

And then we've also covered some things for handling tablet events in NSEvent and specific support for the Apple Pencil.

And again, with so many more tablet users coming to the Mac in the near future, we think if your app can use tablets, it's a great feature to add.

And we wrapped up with the localization enhancements for NS or for foundation geometry data types and then new formatters.

So we hope you're excited to adopt these too.

And please have a great week.

Thank you very much.

[ Applause ]

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