App Frameworks • macOS • 57:40
macOS brings many new enhancements such as automatic window tabbing, enhanced color reproduction, and improved presentation of right-to-left languages. Get introduced to new APIs for these features and more, such as NSGridView and NSMeasurement. Learn about new conventions for APIs in Swift and Objective-C. This is your first stop to discover exciting new features in Cocoa sessions throughout the week.
Speakers: Ali Ozer, Raleigh Ledet, Taylor Kelly
Unlisted on Apple Developer site
Downloads from Apple
Transcript
This transcript has potential transcription errors. We are working on an improved version.
Good morning. My name is Ali Ozer. I and my colleagues [applause]. Thank you. I and my colleagues Raleigh Ledet and Taylor Kelly from the Cocoa Frameworks team will be talking about what's new in Cocoa this year. Our agenda is pretty simple. First, API updates followed by new features in AppKit and Foundation. And in the area of API updates, we have two main categories of API updates I want to talk about. One, the API changes due to the new Swift API guidelines and secondly, the general API refinements we've been doing. So let's get started.
Now as you heard probably yesterday or if not, this morning, we have new API design guidelines in Swift. And these guidelines still embrace Cocoa conventions and practices and I'll just remind you of what some of these are. Using clear and consistent naming, striving for fluent usage, you know, where a call site reads as an English phrase, naming mutating and non-muting method pairs consistently, sort and sorted, append and appending, and avoiding abbreviations.
These are just of the main guidelines we've had for many years and we've documented these and we've talked about them in previous WWDCs, as you see in this session here. And now we updated the guidelines to utilize Swift features such as strong typing, type inference, and overloading, and to make sure our APIs shine through in Swift. And let me talk about some of the key differences in our APIs as a result of these changes.
For one thing, we're eliminating repeated and needless words, which of course makes our APIs more swifty, as you might have heard yesterday. You saw an example yesterday in Chris Lattner's section of state of the union with an example. Here's another example, contacts.arrayByAddingObject. In a method call like this, both the words "array" and the words "object" don't really add much to that call. So such a name now comes across as contacts.adding(person).
Here's another example on the NSColor class. NSColor.blueColor, now we're on the NSColor class. Blue is already a color, so the word color really doesn't add much to this call. So in the new guidelines, it actually comes across as NSColor.blue. But note that we're still striving for clarity. Here's another method from NSColor, NSColor.textColor.
Text is not inherently a color and, in fact, talking about the text of a color could be confusing. Is it the text that represents the color's RGB values or what? So in a case like this, we've actually left the method alone and it comes across as NSColor.textColor, like it used to be.
Now some of the other key changes to making use of types, here's an example, document.readFromURL ofType, now this API comes across as document.readFrom ofType. You'll note that we've dropped the word URL from our method signature. It's because the type of the first argument is URL and with type inference, Swift knows enough to handle that. And by dropping that word, we actually now have three read methods on an NSDocument that are all named exactly the same thing read from ofType and it's that first argument, the type of that first argument, URL data or FileWrapper that distinguishes these from each other.
Now another thing to note here is making use of label on the first argument. From here has now been pulled into the parens. We do this in a number of cases. For instance, when the first argument performs part of a prepositional phrase, we pull that phrase inside the parens, as you can see here. And you'll probably see a lot more examples of this throughout out APIs. Now you'll also notice that second argument ofType.
Here we haven't dropped the word type from the API because that argument is a string and strings are weakly typed. So we really need a word to describe their role and the role here is that this is the type of file we're opening. So there, we keep the word type in the API.
Now many framework and standard library APIs have changed as a result of these new guidelines and the importer whose job is to map Objective-C APIs into Swift now does it using these new guidelines, but you'll note that some APIs may need further tuning. We've done that in our APIs, such as the API textColor, which I showed earlier, which is an exception. And if you yourself have Objective-C APIs you're exposing in Swift, you may need to do so as well by using something like NS Swift Name, which lets you override the default choice the importer is doing.
You also know that the migrator converts your -- There's a migrator that converts your existing Swift 2 code to Swift 3 and it will help you with all this new naming in your existing code, of course. And the Swift API Design Guidelines talk, which took place just before this one, you can catch this on video for a lot more details on these guidelines and their impact. Now we have a number of general APIs refinements, that some of which we've done for a while and others we're continuing to do. Let me quickly go through these.
Nullability is something that we've already done much of last year. It's basically declaring whether arguments, return values, properties can be nil or can be optional or not and we've continued to refine this, this year. For instance, here's a method which in 10.11 was marked as returning an optional value.
This is addItem on NSMenu but in 10.12 it's not marked as returning a non-optional value because we determined that this can never really return nil and it's better to reflect that properly which makes your code, of course, a little simpler. We've been doing properties, converting getter, setter pairs into formal properties all along and this year we're also actually using Swift's ability to declare properties on classes. For instance, here is a new property on NSWindow. You'll hear about this later in the talk, allowsAutomaticWindowTabbing. This is reflected as an actual class property in Swift.
You'll also note that we have an ability now to do this in Objective-C. You can actually declare class properties in Objective-C with this syntax @property class. So the same feature comes across in Objective-C as well, which is pretty cool. Here's a Core Data example NSPersistentStoreCoordinator. It had this method, registeredStoreTypes in 10.11. It's now declared as a class property. It's a read-only property, so it only has a get, as you can see here. And in Objective-C, it also comes across as property class, as you can see.
Now generics, we've been applying them to our collections' classes but they're really, you know, not just for collections but they have many other uses everywhere, I'm sure as you're aware. Here's an example from Core Data, NSFetchRequest. Here's the way it's declared in 10.11, pretty simple. In 10.12, we just went ahead and added a parameterized type ResultType. We also touched a few other APIs and we added some, such as this execute method which returns an array of that result type.
As a result of this, the use case now becomes something like this. You have a request which you get from a managed object, such as employee, and then when you go ahead and execute that, your return now is automatically inferred to be an array of employee. Now this is the case right now but we're hoping that in upcoming seeds we'll actually reflect this a little cleaner like so, which is actually pretty cool. So from those two lines, the compiler is able to infer that the return type is an array of that managed object.
We've been improving our enumeration names. An example that we've done this time around is NSColorPanelMode. The change -- And here's a use case for it in Swift. You'll see that's not very swifty because .NSCrayonModeColorPanel is fairly wordy. The change we've done is to take the common suffix and pull it in as a prefix, which allows the Swift names to come across a lot cleaner. You know, where the point just comes through, and the use case is .crayon. Again, this is what it looked like before and this is what it looks like now, fairly clean.
Now in the enumerations area, we've also now taking advantage of string enumerations. Swift supports string-valued enumerations and in many cases Cocoa APIs expose groups of strings as opened or closed sets of APIs that we use in our APIs. And let's look at an example using string transforms. In Swift 2 we had a bunch of global constants. NSStringTransformLatinToGreek and so on. And then we have an API, stringByApplyingTransform that took those strings as its first argument.
In Swift 3 we've actually added a new type called StringTransform and then an extension we declared various values for that, such as LatinToGreek. And the API that takes that is applyingTransform, where the first argument is now an official StringTransform. So the string argument has now become StringTransform, a little more type safe. Now note that this is an open-ended enumeration in that you can declare your own values. Let me show you how you do that.
You can go ahead and add an extension to StringTransform yourself and define your own type. Now the values here are the values ICU Library accepts for doing transforms. So you can use any valid ID that ICU provides. It turns out Any to Publishing is one ICU has that we don't expose on our APIs. So again, go ahead and define your own publishing transform and pass it to the APIs just as if it's a built-in one.
And string enumerations are also available in Objective-C by using of these two macros for open or close-ended sets. And here's the way the string transform declaration was in fact added. In 10.11, this is what we had. And in 10.12, we have a new type string transform which is decorated as NS Extensible String Enum.
And we're also taking advantage of Swift's ability to nest declarations related about a type, into that type. An example here from NSData. We had a option set, NSDataWritingOptions and class NSData with an API writeToURL that used that value. In Swift 3, this comes across as class NSData with a nested WritingOptions, as you can see here.
And the API that uses it actually just refers to WritingOptions, which is of course much more cleaner, much better scoped. Now unrelated to this nesting change, you'll note a few more things. So that's the change in the type. You'll also you might note that we now provide default values of an empty set most cases where we pass options in, which means you actually don't have to pass the options argument anymore. You might've also noticed that NSURL has become URL here. You might've already heard about this but we'll talk about this more later in the talk.
Now my last topic before I escape the stage for now is noescape. Okay. So noescape indicates that a closure's execution will not escape the function call. What do I mean by that? Here's a method from NSCollection view performBatchUpdates. It takes two closure arguments. You'll note that the first one is marked with noescape. This means that the execution of this closure will finish before this API performBatchUpdates returns.
The second argument is not labeled noescape, which means that the execution of this closure might happen after this function returns. So it's a good hint to you and so a good hint for the compiler as well. This facility is also available in Objective-C where we can decorate closures with NS NOESCAPE to get the same effect.
So that's it for our quick overview of general API changes. Now note in the seed you have, we haven't done everything we want to do yet, so there will be some string enumeration changes, class property changes coming in other seeds as well. So we're going to apply to more APIs. So with that, let's talk about AppKit and I'm going to invite Raleigh Ledet on stage to kick that off. Thank you.
[ Applause ]
Thank you, Ali. Good morning, everyone. As you can see, we've been very busy this past year in AppKit. So let's dive right on in and talk about window snapping. So when you're dragging a window in macOS Sierra, as you bump up against the edges will stick at the edges and as you get along the top edge of another window and even during resizing.
So it's now much easier for you to align windows together and resize them just the way you really want to do. And we spent a lot of effort here in providing heuristics to make sure that we only provide the snapping behavior when that is what you are trying to do with your windows in the first place.
The way you get snapping behavior in your application is to just let the system do the dragging of the window for you. If you track the mouse yourself and manually position the window, you're going to bypass window snapping and we will put the window exactly where you ask us to put it, just as we have in previous releases.
As you've seen in the keynote, we now have window tabbing. This is an example of TextEdit with three document tabs open. And the thing about window tabbing is they are just windows. So this example of TextEdit with three tabs is actually backed by three different NS windows. So what we do is every one of these windows, as long as one of the tabs is visible, is considered visible. So if you check the NSWindow's .visible property, it's going to return true. But only the visible tab is actually being rendered to the screen. We hide all the other windows at the windows server level so they aren't rendered and aren't taking up resources that way.
And we handle this all automatically. So how do you add a new tab to your window? You create a new window and you just order it front and we will go ahead and create a tab appropriately for you and place it in the window. If you want to remove a tab, you order your window out and we'll go ahead and remove the tab. This is all done automatically.
Now if the user resizes the window, we're only going to resize the window associated with the active tab. We don't want to take the processing time to manage the other windows when the users might not switch to them. But when the user does switch to one of the other tabs, at that point AppKit goes ahead and resizes the windows appropriately, re-renders it, and then changes the hidden properties at the window server level, and so the user gets the seamless experience of one window with multiple tabs even though behind the scenes we're just dealing with three windows.
So I want to cover some areas of what you need to do in your applications to adopt tabs into your applications depending on what style of application you have. If you have for example an NSDocument based application or a non-NSDocument based application. What you might want to do if you already have an existing tab implementation, and I'll round the section out with some API that you can use to customize tabs in your application.
If you're in an NSDocument based app, there's nothing for you to do. This is fully automatic and, in fact, in TextEdit here, we made exactly zero lines of code changes in TextEdit. It fully supports tabs and it just works right out the box. If you're a non-NSDocument based app, it's mostly automatic but what you'll probably need to do is enable to New Tab button in your application. The New Tab button is this plus button over here to the right of all the tabs.
To enable that button, you need to implement this newWindowForTab NSResponder override in your NSWindow subclass or somewhere higher up the responder chain. For example, your NSWindow delegate or your NSWindow controller or your NSWindow document. If you implement this override in any one of those places, AppKit will see it and will go ahead and enable the New Tab button for you automatically.
If you have your own existing tab implementation, then you probably want to disable AppKit's automatic window tabbing behavior. There's this class property on NSWindow allowsAutomaticWindowTabbing. This is a class property early in the bring-up of your application called NSWindow. allowsAutomaticWindowTabbing equal false and this will turn off AppKit's tabbing behavior and then you can continue using your own tabbing implementation. It's important that you call this early in the bring-up of your application before you start ordering windows front.
That's all you really need to do to adopt tabs in your application. It works mostly automatically. But here's some of the API that you can use to customize things. First off, the user can define some of the behavior on how they want tabbing to work on their system and they can do this in the System Preferences and you can find out what the user preference is by the class property user tabbing preferences on NSWindow and they can set it to manual, always or to only do tabbing when they're in full screen.
The rest of these properties and functions from here on out I'm going to discuss are instance properties and functions on NSWindow. So once you have an instance of an NSWindow, you can set it to tabbing mode. By default, this tabbing mode is automatic, which means we're just going to follow what the user tabbing preferences are, but you can set it to preferred or disallowed, depending on your window type and your needs.
When we group windows together into a set of tabs, we only want to group windows that are similar and the way we do that is by looking at the tabbing identifier. So windows that have the same tabbing identifier can be grouped together into tabs. By default, AppKit will use a heuristic and try and come up with a tabbing identifier on your behalf.
We look at things such as the windows subclass name, the properties of the window, the document, and various few other properties to try and come up with the appropriate tabbing identifier. This works really well, but if it doesn't quite work well enough for your application or if you want to have more control over it, you can set the tabbing identifier manually and AppKit will respect that.
You can find out what are the windows are grouped together with some instance of a window together in tabs by asking for the tab windows. Note this returns an optional array. It can return nil and it will return nil if the tab bar isn't even shown and there are no tabs associated at all.
And you can manually add another tab window together to the group. Since these are just windows that explains the API name where you add a tab window, instead of adding a tab itself, you add the tab window and you order it using NSWindowOrderingMode and that will order the tab in relation to the other tabs.
AppKit will add a few new items to your menu. Namely in the windows menu, we add some items to go ahead and help the user navigate the tabs, such as selecting the next or previous tab. These are just IB actions on NSWindow and you can wire them up to your own user interface items as well. For example, you might want to have a button that toggles the tab bar. You just wire that button up to the window, toggleTabBar IBAction and NSWindow will handle toggling the tab bar for you.
So that's everything about window snapping and tabbing. Let's move ahead and talk about right-to-left support. We've done a lot of work in AppKit to enhance our already existing right-to-left support. In the screenshot you can see here's TextEdit running in Arabic. The title bar is flipped. The scrollers are flipped to the other side. Even the new tab button is flipped to the other side. And all the little attributes and the various title bar buttons are all flipped appropriately as a right-to-left user would expect.
I want to talk about right-to-left support at three different levels. What's going on at the system level, where the user sets their localization in the system preference pane, how that impacts your application, and what goes on in your application depending on the localizations that you support. And then finally down to the content level, which is at the NSView level where you can override things further if needed with the user interface layout direction. And then I'm going wrap this whole section up with a really nice development tip to help you work on right-to-left support in your application. To start off with, let's talk about the system level.
The key here is consistency. We want to have a consistent appearance to the user who's running in a right-to-left system. So regardless of what localization you may support in your application, we want all menu bars on a right-to-left system to start off with the apple over here on the right side followed with the rest of the menu items and this likewise for the windows title bar.
So the traffic lights are going to be flipped over to the other side and anything that's not in the content area is going to be flipped over to the other side, regardless of what localization you have in your application, providing a consistent experience to the user. Now we can't automatically do this down into the application level, because we don't want to break any assumptions that your application might be having. So it's highly recommended that you add right-to-left support in your application, add Hebrew and Arabic localizations.
And once you have those localizations in place at an application level, all your scroll views will automatically flip the vertical scroller and rulers and NSBrowser will automatically be flipped as well. At this level, again we want consistency so regardless of what content views user interface layout direction may be, we want the scroll bars to always be on the same side for consistency. Moving down to the content level, by default the user interface layout direction is going to match what your application is set to.
This is exactly what you want for almost all cases but there are a few exceptions, namely if you have something like media controls or spatial controls or time controls where it is always laid out the same way left to right for both left-to-right users and right-to-left users. So you can modify the user interface layout direction. And the following controls in AppKit support that. So auto layout will not in this seed but in the upcoming seed two we'll start using the parent container to determine what is the trailing and leading edges.
And already in the seed, table view and outline view will flip their columns appropriately. NSPageController switches its animations and the list goes on. We have a lot of support in AppKit for right-to-left. Let AppKit do the heavy lifting where you can and provide your right-to-left users a great experience.
I promised you a development tip. I've been using this tip all year. I found it incredibly useful. In Xcode, you can modify the scheme of your projects in the Options section, change your application language to right-to-left pseudolanguage. This allows you to run your application in your development language, and for me that's English, and so you can see TextEdit running in English, but it is all using right-to-left flipped controls in it. So the window title bar is flipped.
The scroller for the scroll view is flipped and you can see all the little subitems and all the buttons of the title bar are flipped. So it's a much easier way for you to work on right-to-left support and make sure everything is working properly in your native language.
Now I've just covered what we're doing in AppKit here. There's a lot more to talk about in the What New in International User Interfaces talk in Nob Hill on Friday at 9:00 a.m. They cover more things on the desktop such as WebKit, text layout, asset management, and they also cover right-to-left support in iOS. So I highly recommend that you check out this talk as well.
Let's now talk about promise drags. If you're not familiar, a promise drag is when the user is dragging a file from your application but you don't have this file on disc anywhere yet. But you're just going to promise that you'll write it wherever the user wants to drop it. That's a file promise drag. We've supported file promise drags in OS X since the beginning.
In macOS Sierra, we're updating our file promise drag to be more modern with the new NSFilePromiseProvider and NSFilePromiseReciever objects. Now these objects allow file promise drags to support drag flocking. If you're not familiar with drag flocking, it's when you're dragging number of items and they can change their formation mid-drag, depending on the destination application and what's going on there.
So file promise drags with these two new classes support drag flocking. They're UTI based. They're completely pasteboard writer and reader compliant, which means you can use the item-based API on NSPasteboard to work with these objects, and they're file coordinated when possible. So you don't need to worry about file coordination. We'll automatically wrap up a file coordinated read or write on your behalf. And they're backwards compatible.
They're backwards compatible with the non-item based file promise API. And what this means is you only need to worry about one API. If you implement an NSFilePromiseProvider, you can now provide a file promise to anybody that's using the new NSFilePromiseReciever API or somebody that an existing application that's using the non-item-based API. They will still be able to accept your promised file and likewise with NSFilePromiseReciever. You will be able to accept file promises from anybody using an NSFilePromiseProvider or using the non-item-based API as well.
So you want to provide a file promise in your drag, so as the user starts to drag, you need to create your promises and you do that by creating an instance of NSFilePromiseProvider. You want to create an instance for each one of the files that you are going to promise and you need to provide an NSFilePromiseProvider delegate. This delegate is what's going to do the heavy lifting of writing the files to disc.
At some point the destination is going to call in this promise and you'll be asked to provide the file name for the destination. Now you know where the destination is, you can figure out the appropriate file name, but do not write the file at this point because it's not wrapped up with file coordination yet and we're still figuring out some of the remaining items of the drag.
When the drag is completed and we know all the information we need to know, we'll call your delegate back and ask you to write the promise to URL and provide a completion handler. At this point, it's all wrapped up with file coordination write. So just write the file out to the supplied URL and call the completion handler to let file coordination know that you're finished writing.
If you want to receive a file promise, the first thing you need to do is register with the view that you want to allow the job to occur on. And this is normally done with view.register forDraggedTypes and the drag types you need to add are the NSFilePromiseReceiver. readableDrappedTypes. It's fairly straightforward.
Now once the user is dragging a file promise over that view, you'll start getting dragging messages and you'll want to get the promise objects. So you can use the pasteboard item-based API and just ask it to read the objects forClasses NSFilePromiseReceiver.self and you'll get an array of NSFilePromiseReceiver objects. Once you have an NSFilePromiseReceiver object, you can call in the promise by calling receivePromisedFiles atDestination options operationQueue reader. And it will return pretty quickly from this function and your reader block will not have been called yet. We're going to call that back later.
Once the source file has finished writing all the promises, now we go ahead and we call your reader block back on the operation queue that you specified. Now it's important that you do not specify NSMainOperationQueue for the operation queue or else you're going to block your application while waiting for the source process to finish writing the files. And this can take some time and you don't want to block your application during that whole time.
And that's all there is to do with file promise drags. It's much simpler API to use and it works with drag flocking and supports the item-based API with pasteboard. So let's move on and talk about some improvements we made to our various container views. We'll start off with collection view.
With collection view, previously when you would scroll, your contents would scroll on top of any background view that you might've had. Well, now you can tell the background view to scroll with your contexts by setting backgroundViews ScrollsWithContent to true and they scroll together. We also have support for optional floating headers and footers. So you can see the documents header here becomes floating and the content scrolls underneath it.
This is real easy to do with the NSCollectionView float layout. Just set the sectionHeadersPinToVisible true or the sectionFootersPinToVisibleBounds true if you want the footers to float and NSCollectionView will then take care of doing everything appropriately on your behalf. You can also optionally collapse any section into a single horizontally scrollable row, as you can see here, and you get this nice little carousel effect whenever you scroll horizontally.
This is real easy to do with the toggleSectionCollapse IBAction on NSCollectionView. Now the sender of this message needs to be a view that is at your section header or a descendent of your section header view and that is how NSCollectionView can determine exactly which section needs to toggle the collapse state for.
Since you have your button in your header view or descendent, you should have your section header implement NSCollectionView SectionHeaderView protocol and that's just to wire up your button to the sectionCollapseButton outlet. Then NSCollectionView will now be able to find your button and automatically hide and show it, depending on if there is enough data inside that section to collapse.
NSTableView will now reload full width cells when the column index is negative 1. Negative 1 is our magic number to say a column that expands all the way across your table, across all columns. So when you call reloadData and columnIndexes includes the negative 1 in there, we will automatically reload that full width cell on your behalf. This only works for 10.12 and later linked applications. Likewise on 10.12 linked applications or later, NSOutlineView will also automatically reload its cell views associated with the item you pass in to reload item.
Additionally, OutlineView now strongly references the items you return to it via the data source. This is really useful but if your application really wants to maintain the original assigned behavior that we had previously, you can set stronglyReferencesItems to false and return to the pre-10.12 behavior. And with that, I'd like to bring up Taylor Kelly to go ahead and talk to you about grid views.
[ Applause ]
Thanks, Raleigh. So the first thing I'd like to tell you about is NSGridView. This is a new container view class, similar to NSStackView where we completely create and manage the constraints necessary to build your layout. But where StackView creates a linear distribution of use, NSGridView creates these intersecting rows and columns. You very commonly see this in something like a preference pane UI. Right, there are these distinct columns and rows of aligned content that are self-sizing and pretty statically defined.
It supports alignment such as baseline, leading, and trailing. It supports spacing of the GridView as a whole as well as its padding on an individual row and column basis. Similar to a spreadsheet application, it supports cell merging. So a single view can span multiple rows or multiple columns. So for instance these separators do just that.
It also supports dynamic hiding and showing of rows and columns without changing the GridView's own structure. So for instance, when we click on this checkbox, we might want to hide the Display Preference button. And we can do that by getting the row containing that button and just setting its hidden property to true. GridView will take care of the rest.
There are several other improvements we've made to Auto Layout. One is a cleanup of overall AppKit layout cycle meaning that a view no longer needs to be using auto layout or layer backing in order to participate. Just by setting needsLayout to true on that view, during the next display pass it'll receive a call to layout.
One implication of this is that layout is no longer called twice per display pass for layer-backed views and we have less implicit dirtying of that layout and less layout passes because of that, which is great for performance. But if you notice that layout isn't getting called on your view when you expect, make sure you're explicitly setting needsLayout.
All of this makes it easier to do manual layout of your subviews. So you can overwrite layout without calling super and instead just calculate the frames of your subviews, set them, and return. You do want to be sure that you don't dirty that view's layout or other views. This can happen if during a view's layout it sets its own frame or the frame of an ancestor. This will dirty that view's layout and cause an additional layout pass.
If this continues, this is what we call a layout loop and these are pretty difficult to debug. So on macOS and iOS, we've introduced new layout loop debugging support so you can better understand what view is dirtying layout and why. There's changes to the layout constraint API adding anchor properties so you know what anchors that constraint refers to. These apply even when you're not using the anchor-based API.
In addition with that first time it is now nullable, so if you are reading that property, be sure to handle the nil case. In addition, Interface Builder has new support for incremental adoption of auto layout. So within a single document or even within a single view hierarchy you can partially adopt constants, which is pretty great. NSGridView, the layout loop debugging and more, is talked about in Friday's, What's New in Auto Layout talk.
At this point I'd like to talk about colors, specifically wide gamut colors. So sRGB is a pretty popular color space. Usually it's the implied color space of otherwise unspecified and most of our displays have an sRGB color gamut. The new 5K iMac and iPad Pro 9.7 inch, both contain these updated displays that can display more vivid colors.
Specifically, they're using the P3 color gamut and this is what we call a wide color gamut because compared to sRGB, it can display much more vibrant greens and reds. So your applications can display photos much more accurately to their original color and they can display these more vibrant colors to make UI elements really pop.
An example of such a color would be this emerald green, which happens to be Pantone's 2013 color of the year. It's outside of sRGB. So previous displays couldn't represent this but it is fully representable using P3. Your photos also contain these more vibrant colors, especially these reds and oranges. A note of irony here is that the projector and even the video at home are not actually going to reproduce these colors, so you'll kind of have to take my word that they're special.
If you want to work with these colors, there's a new color space displayP3, which lets you work with this. This joins sRGB and other color spaces. There's also a new constructor on NSColor displayP3 red green blue alpha which allows you to create a color in that color space. UIColor has the same constructor with the same semantics. Another way of working with wide colors is using something we're calling extended range sRGB.
And so when working with a color space, you're typically constrained with component values from 0 to 1. And you can pick any color within that color gamut, such as this blue, with those values. But you can't represent a color that's outside of that color gamut. So sRGB cannot represent this green color.
Well extended range sRGB uses the same color primaries and the same white point as sRGB but allows the components to take on values less than 0 or greater than 1. So it can represent this green with a negative red and blue value and a greater than 1 green value.
It happens to also be the P3 green primary, so you can see that with extended range sRGB, you can represent the full range of colors necessary for these displays. This also has a color space extendedSRGB and the existing NSColor and UIColor constructors you're already using that previously gave you colors using sRGB. Will now accept values less than 0 or greater than 1 and give you back an extended range sRGB color, which is pretty convenient.
One important thing to consider with these wider color gamuts is color depth. So sRGB can theoretically represent any color within its gamut, but with 8 bits per component, you can only address a finite number of those, right. The black space here are simply un-addressable colors using those 8 bits per component.
When you widen the color gamut, right, increase the volume of that gamut but keep the same bit depth, you're addressable color density goes down. Your ability to specify a color loses precision. So this is why we recommend when working with these wider color spaces, such as P3, that you use 16 bits per component instead. It doubles the amount of memory and storage but gives you exponentially more addressable colors. So we think that tradeoff is worth it.
What API do you need to use in order to consider these deeper colors? Well, the great news is that most of it is automatic. So NSWindow will automatically use deeper backing stores with these higher bit depths on wide gamut displays, even as you drag it across screens. If there's a reason you need to explicitly control that, you can set the depth limit property to some value of your choice. Views and layers within that window will automatically inherit that bit depth with the exception being OpenGL view. There you should use the associated pixel format API.
CALayer on both macOS and iOS has a new contents format API property that allows you to again explicitly control the bit depth it uses. So this is how you can take advantage of wide color in your application. We've also enhanced part of the system to better take advantage of these wider colors. One example is the color panel.
So on the iMac on the left here, the color wheel will actually display, there we go. It will actually display and allow picking from the full range of P3 colors beyond the sRGB that normal color wheels allow. However, you can also right click the color wheel and explicitly choose the working color space. So even on these previous displays that can't render P3 colors, you can still allow picking from them.
The RGB color picker has always allowed you to choose the color space you're working in, but it also allows you to change the representation from 8-bit values from 0 to 255 to floating point. So again you can get this higher precision when working with these wider color spaces. These color panel changes comes to all applications completely for free. No API is needed for those. The Working with Wide Color talk on Thursday covers this and more, such as asset catalog support and WebKit support. I'd recommend checking that out if this stuff interests you.
So the next topic is status item enhancements. So status items are the things that live in the upper trailing corner of the menu bar and these are things that previously required the use of private API to achieve but now come mostly for free. First is reordering. You can now command click and drag on any item and reorder it within and beyond the system items. You can also use keyboard focus to navigate to your item and even activate menu items within.
The best part of all of this is that this comes completely automatically with no API opt-in or link check. So all items are command-click and drag to reorderable, and any items with a menu set will participate in keyboard navigation. If they have a custom target action, they'll just be skipped over.
The next is hiding and removal. You can now command-click and drag an item out of the status bar, remove it, get notified of that change, and even programmatically restore it. Unlike reordering, this does require opt-in. No status items will be automatically removable and you can do that by setting the behavior of the status item to include removalAllowed.
You can programmatically read/set and even get KVO notified of this change using the isVisible property. And if your application is a status bar app, meaning its only representation is that icon in the status bar, there's no doc icon or other way to quit it, you can set it to automatically quit on removal by setting the terminationOnRemoval behavior.
All of this gets autosaved for you, the location and the visible state so you don't have to worry about trying to preserve that using the autosave name. We automatically generate this based on the item index that you created in your application, but if you create them in some non-deterministic order or just want to have more explicit control, you can set that autosave name to some identifier of your choice.
So that's status item enhancements. A lot of it comes for free, and with a little bit of tweaking, you can really make them great in your application. Next are control constructors. So these are new constructors on existing NS Controls that make it really easy to get standard look and feels. Examples are different types of buttons, segmented controls, image views, sliders, and labels, and text fields. So these are the types of things you're already working with in the Interface Builder object library and it makes it just as easily to use these right out of the box.
They come with the standard system setup, the right font size, and text color, and they support different contexts. So the label here looks great in aqua, vibrant light and even a vibrant dark all right out of the box. There's no additional setup needed. I'd love to show you an example of what this does to your code. So this is creating a checkbox before. There's quite a few properties to set and the unfortunate thing is we really only care about three of those; the title, target, and action.
Well with checkbox, title, target, action, it's distilled down to just that, which is pretty great. A bit more of a -- Oh [applause]. Just wait. So this is creating just a static label, right, just some text on the screen. And here, we care about just the string value. Well, with labelWithString, it again is just that. So, this is pretty great. It comes out of the box --
[ Applause ]
It comes out of the box ready to be used with or without auto layout. So if you are going to be positioning with constraints, you still need to set translatesAutoresizingMask IntoConstaints to false, like all of your other views. And so if you already have categories doing these kinds of things, we still recommend moving over to these new system ones because you'll ensure that your controls have the standard look and feel both now and in the future.
The last thing I'd like to talk about are API refinements. I only covered a number of these that applied to all of our frameworks and these apply to AppKit as well but there's two more that I want to talk about that apply to AppKit specifically. The first is weak delegates. So we've added new zeroing weak delegate support for various delegates and data sources for different classes, so you no longer need to clear these properties once the delegates is deallocated.
It still supports non-weak referenceable objects in which case it'll fall back to the existing assign or unsafe, unretained semantics. We've also gone through and made sure that all of our classes explicitly did declare their designated initializers. These are the same as what they effectively were previously but now are just declared in the actual API with the one exception being NSCursor. So if you are subclassing NSCursor, please see the release notes for how you should properly deal with that.
As with all newly declared designated initializers, you should make sure that you're properly dealing with that in your subclass. And so if you weren't previously, you potentially had these incorrectness issues that maybe were subtle bugs. In Objective-C, you'll now see build warnings for this. And in Swift, you're going to get build failures. So you want to make sure that you're properly handling these different cases. So that's it for what's new in AppKit. At this point, I'd like to turn it back over to Ali to tell you about what's new in Foundation.
[ Applause ]
Thank you, Taylor. So here are some of the things that are new in Foundation and let me just dive right in. Now earlier you saw NSURL becoming URL and I think we also talked about it a bit yesterday. We are dropping NS prefix in key Foundation types in Swift.
Now, as you might know, large subset of Foundation actually ships with the Swift Core Libraries as a part of Swift Core Libraries and is available in other platforms, such as Linux. And we want to match the naming style of this part of Foundation with the convention established by the Swift Standard Library which does not use prefixes.
As a result, a lot of our types drop their NS prefixes in Foundation and Swift, as you can see. NSFormatter becomes Formatter and so on. Now the last two here, NSData and NSURL are part of a special category and I'll mention these in a little bit. Now, this is happening in Foundation only.
It's not something we're applying to our other frameworks, and even in Foundation, it's only applying to some APIs. We do not apply this NS dropping to APIs that are inherently tied to Objective-C. Examples are NSObject, NSProxy, NSAutoreleasePool. We also do not apply it to APIs that at platform specific and are, in fact, not available on other platforms such as NSUserNotification, NSXPCConnection.
And in one other case is classes which are also exposed as value types. So here we are exposing data, URL, et cetera, but we're also exposing NSData, NSURL, and so on and let me talk about what I mean there. Now first let me explain a bit about value types.
You might already be familiar with value types. We've had this concept in Foundation for a long time. These are types where value is important, not the identity. Examples are NSString, NSData, URL, Array, et cetera. Now, since a number of Foundation APIs already have these value type semantics, we've gone ahead and added them as value types in Swift. And here is the full set of APIs where we've done this. This is in addition of course to string, array, dictionary, and set which are already exposed as values types in Swift, since they're available in the standard library.
So now these types here are exposed as structs in Swift and they have value type semantics, meaning they can be directly mutated, if mutability makes sense for them. You can use let or var on these to distinguished mutability at the time you declare them. These also conform to the expected Swift protocols as well, as you might expect.
Now existing class APIs still remain in cases where we've created these value types and let me look at a case study here. An example is data, the one I already mentioned. So now we have the struct data type. This is the value type for data. This is the data type we expect you to use most often in your programming. This is the type moving forward; however, we also have NSData which is a subclass of NSObject and NSMutableData which is a subclass of NSData itself.
Now NS types remain because something like NSMutableData is not migratable very easily, so the migrator will not migrate it. Another reason we have NS types is sometimes you actually do want to subclass these types. You know, you might be subclassing NSData or NSMutableData to provide some specialized implementation and you can do that with, of course, the class types, as you see here. So these are some of the reasons why the NS types still remain.
Now, let's look at some differences here. The NSData class has a length property. This has been named count in the struct version because it's more consistent with the rest of the Swift Standard Library. Methods such as write to, range of and many of these other methods remain pretty much intact.
They come across the same way except you'll notice that the NSRange and NSData has become range of index in the struct data version, which is more consistent with the Swift Library. And finally you'll note that a mutating function, like append, is not available on this value type data directly while in the case of NSData, it's on the NSMutableData class.
So these are some of the differences you'll see between the value types and the class types. Now I'm not going to say much more about this. There is a talk this afternoon, What's New in Foundation for Swift in the Mission room. I encourage you to attend that and hear more about these.
Now we have several new types in Foundation to represent measured amounts. The main class here is the unit class. This is an abstract type to represent units, such as miles, degrees Celsius, kilometers per hour, and so on. And then we have a class dimension, which is a subclass of unit. This represents unit families such as length, temperature, and speed and so on. So here we have the unit class. We have the subclass dimension and then we have subclasses such as unit length, unit temperature, unit speed, and a bunch more.
There's a helper class unit converter that allows conversion between units within the same unit family and there is a struct measurement, it's a class, of course, in Objective-C but across as a value type in Swift, struct measurement which combines a value such as 10 with a unit such as miles, so this basically is now a measurement, and finally the icing on the cake, we have the measurement formatter class which will take one of these measurements and show it to the user in the user's locale. So it'll show 10 miles to a user here in the United States but will show 16 kilometers, for instance, to a user in Europe, who's using the European system. So it will do the conversions for you and show the user the right thing.
[ Applause ]
Now out of the box, we have plenty of unit families defined. Here's the whole set. So these are subclasses of dimension, and within each one of these unit families, we actually have multiple units defined out of the box as well. Let me take a look at unit temperature for instance. You'll see that unit temperature has three standards units; kelvin, Celsius, and Fahrenheit. And these are defined as class properties on the UnitTemperature class and many of the other dimensions also have a number of units defined that know how to convert between each other.
And even better news, you can actually add your own units on top of what we provided and they'll play with what's already there and you can also add your own unit families as well. So you can hear much more about this Friday afternoon at 4:00 o'clock, Measurement and Units.
DateInterval is a new type we've added to Foundation. It represents a date interval. It's got three properties; start, end, and duration. Fairly straightforward, so these are not, of course, fully independent but they're reflected as three separate properties. In addition to these properties, DateInterval knows how to do things like check if a date is within a date interval or whether two date intervals overlap and so on.
Date intervals are of course also very useful to be formatted with the DateIntervalFormatter class. That's something we -- That's an API we introduced last release. We had an API string from to. Now we have the string from API which simply takes a date interval, so fairly straightforward. Now at this point let me give you a public service announcement about handling dates and times.
So handling dates can be tricky. Now this is not relationship advice here. Okay? I'm not a doctor. Let's say you want to represent a ten second period. You might go ahead and create a DateInterval like this, start date and a ten second period. This is likely correct for whatever you might try to, whatever you're trying to do with a ten second period, but let's say you're trying to represent a day.
You might go ahead and write code like this, 24 times 60 by 60 which is the number of seconds in a day. Well this is the number of seconds in a 24-hour period and it's often not going to be correct. The reason is because days are not always 24 hours long. Months aren't always 31 days. Years aren't always 365 days and so on. We always keep those in mind but there are also days which are 23 hours or sometimes 24 hours, 25 hours, as you know.
So depending on what you want to do with a DateInterval representing a date, are you trying to set an alarm exactly 24 hours from now? Are you trying to send an alarm at the same time the next day? You have to be careful. Typically the correct solutions here involve using the calendar class and you can hear about these problems and also luckily their solutions in this talk from 2013, Solutions to Common Date and Time Challenges, which I encourage you to go back and watch. ISO8601 DateFormatter is another new API in Foundation. It's a formatter for dates. Thank you.
[ Applause ]
So clearly some of you have had to use 8601 before; 8601 is a standard. It's an interchange format for specifying dates in an unambiguous manner. So this is a separate class than DateFormatter because unlike DateFormatter, which is meant for user-localized dates, 8601 formatting is non-localized. It's, you know, interchange format. So it's a separate type. We decided to keep things simple. Using it is very simple. So create a formatter.
Get yourself a date, again not relationship advice. And simply ask the formatter for the string for that date and it will return you the format. Now this DateFormatter can actually go both ways, so you can actually -- Oh, by the way, here's the output from that call, and as you can see, it's getting close to lunchtime.
And here is -- This formatter also goes the other ways, also does parsing. So you can get yourself a formatter and ask for the date from a string and it'll return the date for you. So it goes both ways. Now by default, this will do RFC 3339, which is one format; however, there are options that let you specify some of the behaviors, if you need to.
Now quickly let me cover some other Foundation updates we have. URL has a bunch of new properties, such as canonical path of a file and a bunch more, like whether a volume is encrypted and so on. You can read all about these in the release notes. There's a new class URLSessionTaskMetrics. This class helps you gather network resource loading performance information so you can actually look at the network performance of your applications.
PersonNameComponentsFormatter is an API we added last release. As you know, it takes a person's name and it formats it in a locale-appropriate manner. Well in this release, it actually can now parse names. So you give it a name and it will return to you the first name, last name, et cetera and it does a pretty good job since it uses a statistical model based on real-world data.
And finally -- But there are some tricky names out there. So don't get your hopes up fully. And DateComponentsFormatter, again this is API we added last release. In addition to the full style and the short style, we now have a brief style of date that you can format. And next release, maybe we'll add the boxer style as well. Okay, so that's it for Foundation.
Core data, just a quick mention of core data. You already saw some API improvements in core data with generics. There's a bunch more new APIs in core data, such as generational querying, persistent store description, the NSFetchedResultsController class is now available in OS X as well, I'm sorry, macOS as well.
You can hear all about this Friday at 10:00 a.m. What's New in Core Data. And one more talk I want to give a shout out to, every year we get up here and tell you about new APIs, new technologies were introduced and you might not always be in a position to adopt these APIs because you're in the middle of something else or maybe you wait a release and so on.
Well, if you want to get an overview of recent APIs we've added, APIs are important to create modern applications for the Mac. This is talk for you. It will cover a lot of topics and they'll have pointers to other sessions of interest, not just in this WWDC but also prior WWDCs as well.
It's also appropriate for everyone, all ages, all experience levels. It's also Friday at 5:00 o'clock. So I hope you're here. Okay, so and here is the webpage you can go to for more information. Please read the AppKit and Foundation release notes, which you can find in our developer tool site. It's there, just raw information about a lot of the stuff I talked about. Here are the related sessions we mentioned. There are of course many more. Thank you very much.