Examine the technologies that make a modern macOS app. Come learn from the experts how to harness the power of Cocoa to build powerful, immersive apps. Whether you're a seasoned Cocoa developer or new to the platform, this session is for you.
[ Music ]
Hello, welcome to Crafting Modern Cocoa Apps.
My name's Corbin Dunn, and I'm going to give this talk
with my colleague Jeff.
We're both AppKit software engineers.
So let's jump right into it.
What are we going to talk about today?
We have a whole bunch of subjects.
We're going to talk about getting a modern look,
drag and drop and how to do it correctly,
container view controls, table views,
using some system appearances, designing with storyboards
and some other cool Mac features.
Tons of stuff which we're going to highlight
with a quick demo app that we have
that we see in this screen shot.
How it's alive in new features
or existing features they can adopt.
And all these technologies, most everything you can use already
on Mac OS 10.10 or 10.11, anything new
to 10.12 we will specifically highlight and point out.
And this demo app we encourage you to download it.
It's not quite available yet but it will be soon,
and it will be associated with the talk.
So let's jump right into it and talk
about creating a modern look with modern views
and what you can do there.
So modern look means a modern window and a modern toolbar
such as shown in this demo app here.
One thing you want to be doing is using a full size
The full size content view allows your content view
to extend underneath the toolbar area
that I'm highlighting here in white.
And that means that full size content view will have its area
automatically blurred by the titlebar and toolbar area
without you having to do anything.
Using a full size content view is really easy.
It's just a window style mask.
You can use the .fullSizeContentView bit
on it to include it.
Or if you're using Interface Builder you can just check a
check box, full size content view, and you get it.
So if you're using the full size content view you may need
to offset your content underneath it.
For instance, let's say that we have something
that we want underneath that titlebar/toolbar area.
We might want to put like a label or something there.
If we put that label there how do we get it
under that titlebar/toolbar area which might have, say,
a dynamic height that would change, and we want that label
to not have a hard coded Y offset
because hard coding offsets would be really bad.
So what we have is we have the content layout rec property
This is a key value observable, a KVO observable property
which you can use to find
out whenever the contentLayoutRect the area
in turquoise changes.
So you can go and place your view directly underneath the
titlebar/toolbar based on that.
But we also encourage you to be using auto layout.
And if you're using auto layout you can use the
contentLayoutGuide which is a property on NSWindow.
For instance, inside of your NSViewController subclass you
might be overriding updateViewConstraints,
and you want to create a constraint.
You grab the window's contentLayoutGuide,
tick the top anchor for it, and you have your text field,
and you create a constraint
to that text field's top anchor also and activate it.
And if you do that, that text view will automatically be below
that titlebar/toolbar area
without you having to do anything.
It's very simple to do.
Next for a modern app you might be wanting
to use a streamlined toolbar.
So here's a screen shot of a normal application.
The things to notice here is it's got an explicit titlebar
or, sorry, it's got explicit title up at the top,
and you would have an icon there
if the app actually had an icon for this document.
If I go to using a streamlined toolbar that title disappears,
the toolbar is pushed up a little bit,
and the window buttons, the close button, etcetera,
are directly in line with that titlebar/toolbar area.
To do this it's really simple.
And all you have to do is set the title visibility property
to .hidden, and that title will be hidden,
the toolbar will be pushed up, and everything will be in line.
When do you want to do this?
You probably want to do this for applications that are kind
of like shoebox applications, for instance maps, calendar,
system preferences, things of that nature that are kind
of like one window applications are good for this type of look.
So what other things could you want to do?
Well, you might want to complement
that titlebar/toolbar area with accessory view controllers.
For instance, you might want
to have an accessory view controller that's right below
the titlebar, and by doing this you want it
to automatically be blurred
without you having to do anything.
The size to be automatically changing
as the window is resized without you having to do anything.
So how do you do this?
It's really simple.
We have a view controller subclass,
called NSTitlebar AccessoryViewController you can
set the view to that view controller.
In addition, it exposes a layout attribute.
So the layout attribute could be set to .bottom which means, hey,
this accessory is going to be on the bottom of the titlebar area.
And notice the text appeared below it because, again,
we're using the proper content layout wrapped
in content layout guide without having to do anything.
In addition to the bottom, the layout attribute could be set
to leading or trailing.
And this means you get a accessory view controller
up in your titlebar area such as a register me button
or something else that you might want up there.
New to macOs 10.12 is the ability to use leading
and trailing as opposed to using left and right
which were published before.
We prefer you to use leading and trailing
because it allows your window to work better
when you're using a right to left localization.
So, for instance, if I'm just running this in pseudo right
to left mode you can see
that that accessory view controller automatically flips
to the other side without having to do anything.
Next we, the system,
may actually be adding our own accessory view controllers
to your window.
For instance, we might be using it for creation of tab windows
where we aggregate multiple windows
into one sort of virtual window.
So how do we do tab windows, and what do you do as a developer?
You don't really have to do anything.
If you order a window front what we do is we look for windows
that are similar to that window.
And by similar I mean we look
for the tabbing identifier property.
If it has the same tabbing identifier we're going to prefer
to aggregate those windows into one single tabbed window.
The windows themselves are all considered visible even though
it might be in a hidden tab.
But we actually hide them with respect to core graphics.
If you're using NSDocument, a lot of the things
such as the plus button inside the tab bar work automatically
without you having to support anything.
But if you aren't using NSDocument you can implement a
responder chain method new window for tab
to create a new document or a new window on the plus button.
For more information check out the, What's New in Cocoa talk
to see more details on this.
For more information on the titlebars,
the accessory view controllers and how they interact
with full screen, I highly recommend seeing the talk
from 2015 on improving the full screen window experience.
Next let's talk a little bit about core animation.
So what is core animation?
Well, it's a graphics rendering engine that does a lot
of the work on the GPU as opposed to CPU.
So everything can be very fast for scrolling.
We can do very fast and smooth animations.
And the base component of this is a CA layer.
How we actually create your layout
and your views using layers or views themselves.
So this is an example animation where you might have
like a background layer, a middle layer,
and then a front layer actually doing an animation.
So you want to compose your views of multiple subviews
to create the final look.
Let's take a look at some of the properties in core animation
or CALayer that allow you to set the contents
of what you see on screen.
Speaking of contents, CALayer has a contents property.
The contents property can be an NS image or a CG image ref
that actually represents what you see
for that layer's contents.
But you could also set other properties on CALayer
such as the background color, the border color,
the border width, the corner radius.
There are more properties you can take a look at CALayer
to see the other things that are available and how to control it.
So these are some of the intrinsic basic ways
to actually set how a CALayer will look.
And we're going to talk a little bit more about this in a second.
But what you should be doing is you should be using a layer
So you're going to be using NSViews set once layer to yes
which I'll show in a second how to do that,
and that will implicitly create a layer for you.
And that means that you can provide the layer contents via
But if you're using layers we'll actually have two more important
methods, updateLayer and wantsUpdateLayer.
And in just a second I'm going to show how these come into play
and when you'll want to use them.
So what do we actually recommend for using core animation
or when should you use core animation and layer backing?
Well, we recommend that you layer back your windows
And when you layer back one particular view all the children
will automatically get layer backed for you, too,
without you have to opt into a layer
for every individual child view.
We recommend that you layer back views
and you not use CALayers directly added as a sublayer
because that takes care of some things automatically for you
such as when a layer is shown on a retina display.
We'll do some more setup that takes care of the work.
Turning on layer backing is really simple.
On the content view of your window you can set wants layer
to true and code.
Or, of course, you can just go ahead inside interface folder
and check the checkbox for Core Animation Layer
on a top most view.
So an important property
on layer backed views is the layerContents RedrawPolicy.
You want to set this .onSetNeedsDisplay
which is not the default value for NSView.
What this means is you
as a developer whenever you want the contents of your layer
or the view to change you have to call SetNeedsDisplay.
This is slightly different semantics
in the way normal views would work
where they might actually redisplay as they're moving
across changing a frame origin.
So it's something you need to explicitly opt into,
and it's better for performance particularly with animations.
So how do you actually get contents into your layer?
So let's say your view is dirty.
You mark SetNeedsDisplay.
And what we do is we ask your view, hey,
what do you do for wantsUpdateLayer?
What's your answer for wantsUpdateLayer?
And here we fork.
If you say yes
for wantsUpdateLayer then we're going to call updateLayer.
If you say no we're going to call drawRect.
So this is a complete fork.
It's an either/or.
Then you may be wondering, well,
when should I use updateLayer versus drawRect.
This gets back to what I was showing about core animation
and NSLayer properties.
So if you can represent your views representation directly
by setting CALayer properties then you probably want
to use updateLayer.
It's going to be more efficient.
So if you can set the layer contents,
layer background color, prefer to do that.
If you can't, then that's the point where you want
to override draw or drawRect
and actually do your manual drawing inside of the subclass.
The thing about doing this is that every view
which implements drawRect will get its own unique little
backing score which you can think of as an image.
And so if you have a lot of those it might be expensive
in particular for memory.
Let's take a quick look at an example of using update layer
and how you would use it.
So first of all you'll override wantsUpdateLayer,
say that you want to get an updateLayer callback
by responding with a true.
And then you're going to get a callback to update layer
where you can actually set the layer properties.
So we access the view's property, self.layer contents
and the self.layer backgroundColor
or whatever other things you want to set,
and you set it at this time.
So that was talking about modern views and creating a modern look
for the titlebar/toolbar.
Let's talk about drag and drop and do some event tracking.
So modern drag and drop,
what you should be doing is adopting drag flocking as shown
in this video right here.
Drag flocking is where each individual item will move
independently and flock together or unflock together.
And when you let go of the mouse they'll actually all flock back
to where their original location was.
To use drag flocking you just use NSViews method
beginDraggingSession, and you pass an array
of the actual items that you want to have be dragged around.
It's very simple to do.
For more complex controls like a table view
or collection view we provide delegate methods
for you to adopt these.
So for table view you should really be preferring the
tableView pasteboardWriter ForRow
where it can provide an individual NSPasteboard writing
item for every item in a table view that's being dragged.
As opposed to the older method, tableView writeRowsWith
to a pasteboard where you would have written everything
in one go to the pasteboard.
Similarly, collection view has two delegate methods
that look very similar to that.
And for collection view you should also prefer the
pasteboardWriter ForItemAt version
as opposed to the other version.
So with drag flocking new to macOS 10.12 is the support
for drag file promises.
This may be a reason why you weren't using drag flocking
up until this point.
And you can use NSFile PromiseReceiver
and NSFile PromiseProvider to do reading
and writing of file promises.
Take a look at the talk, What's New In Cocoa
for more information and more details on this.
So let's take a look at event tracking.
And let's say you have your window here.
And let's say it has a button up at the top.
So you have this button.
And what you want to happen is that when you click
on that button down and up it works like a normal button.
You will do some action like showing a popover.
But you also want to be aware you click on that button,
and if you actually drag the mouse it starts a window drag.
So how to get both those behaviors together in a proper,
modern way that works well with modern system features?
Well, first of all let's talk about event tracking
and how you'll track events to do this.
And this window has a method track events matching mask
with a timeout mode handler.
And the handler is a block callback.
We prefer that you use this as opposed to the older method
on NSApplication which was nextEvent matchingMask.
With the older method you would create your own loop.
With the newer method you just get a block callback
to do your work.
Taking a look at a block callback let's say
in that button we subclass and override mouseDown.
The first thing we're going
to do inside this mouseDown is call window track events.
We want to track all of the events
for the drag and the leftMouseUp.
If the user did a click, just a down and back up,
then we're going to just be like, oh, we're going to stop
and we're going to say, oh, we're going to call yes
for super because that way everything will work normal
like an NS button.
But here's the extra part.
Let's say you click and you drag, and if you click
and drag inside this button we're going to figure
out if you went far enough, if you went far enough we're going
to pass off to the window to do a performDrag.
And so what that means is when you pass off to the performDrag
of the window the window and the system is going to take
over dragging the window at that point.
You should not be dragging and moving a window
by calling set frame again and again on a window.
If you pass off to the system as soon as you pass it off,
if your application hangs, then that window will continue
to be moveable even if your application is spinning.
In addition, other system features will work
such as space switching, the spaces bar will drop
down when you move to the top of the window,
window snapping, window alignment.
Any other new system features we add
with window movement will automatically work
without you having to do anything.
So we encourage you to pass off to the system
by calling performDrag.
So that was talking about drag and drop,
a little bit about event handling.
Let's talk about container view controls and how
to handle those properly.
So container view controls we should be using the view based
table view at this point.
And you do that by using the delegate method table view view
40, or inside of an interface builder you can set things
up directly in interface builder itself.
And why you want to do it is so you can get what we saw
on that video new features such as swipe to delete
which only exists in the view base table view.
To do the swipe to delete is very simple.
There's a table view method, row actions for row
on a particular edge, the left or the right edge or, sorry,
leading or trailing edge.
And you can return an array of one or more
or a zero or more row actions.
In the NSTableView row action allows you
to create a string value for what the title is of the button
and a handler to actually do the code that happens
when the user clicks on the button or swipes far enough
to actually invoke it.
Next let's talk about ScrollView and that complex control.
So for using all these technologies we talked
about let's say that we take the ScrollView --
or let's say we take the window and set the window property,
titlebar appears transparent
which makes the titlebar appear transparent.
Applications like Messages take advantage of this
to allow the contents to show underneath.
So here we can see what's happening.
That ScrollView on the side is showing the content underneath
the titlebar/toolbar area
and would automatically blur with it.
But this presents a dilemma.
How do you get that ScrollView to automatically be inset
so that you don't have to add in like an extra empty row
or anything really weird?
And it's very convenient and easy to do this.
ScrollView has a method or a property called contentInsets
that allows you to drop the content down a little bit.
In fact, we can do it automatically
so we have a property automaticallyAdjust
And if you set that to true then what we're going
to do is ScrollView is going to use KVO
to track the contentLayoutRect that we talked about earlier
and automatically set the content insets
to be the appropriate value for you.
But you as a developer may actually want more control
of the content insets.
You might want to drop it down even further
and add some other accessories there such as
like a search field or something else.
And an example of where we do this in the system Mail drops it
down a little bit and adds another sort indicator.
That's how we accomplish things like that
in our system applications.
Next let's talk about auto layout.
You should be using auto layout,
and you should be using base localization.
Which means that all your nymphs should be in base.lproj instead
of multiple copies of different folders
and different localizations.
You shouldn't use fixed width constraints.
You want to use controls to have intrinsic content sizes instead
of hard coding sizes.
Prefer to use StackView.
Use leading and trailing attributes.
All these things are right
for creating a properly localized application.
But let's take a look at some of the localization options in IB.
In particular let's look at this Text Direction,
Layout and Mirror section.
So the text direction has three values.
It has natural, left to right and right to left.
Natural means that the actual control is going to look
at the string value that you set on that control
like a text field, look at the string value.
And if that string is a right
to left string then we will actually put
in the direction right to left.
If it's a left to right string we'll put it in left to right.
Or you can manually control it by setting these to left
to right or right to left.
The next property is the userInterface LayoutDirection
which is key to layout.
And it has either left to right or right to left.
System controls such as table view will look at this property
and may do things
like automatically flipping the table columns
when it's set to right to left.
The default here is based on the app value.
But the interesting thing is the mirroring property
because it's a little confusing.
So if mirroring is set to automatically then
that userInterface LayoutDirection,
the last property we just looked at, will automatically go
from left to right to right to left when it's in a right
to left localization and vice versa.
And it will also flip other properties automatically
like the cell image position for a button.
So the button's image is on the left and we'll flip it
and put it on the right for you automatically.
But one property that we won't flip is on text alignment.
If you have it set to center, justify
or natural those don't really make sense to flip
so we won't flip them.
So then you might be wondering, well,
when do I actually not want to have mirroring?
Well, you might want to have mirroring set to no
when you want a control that has a very physical representation
like a play button,
a fast forward button or a rewind button.
And then these are all interface builder things
so how do you actually do this in code?
So in code you actually have to look at your controls value
for the user interface layout direction.
If it's set to left to right then you're going to have
to say, hey button, I want that image to be on the left
when it's in a left to right localization.
And I want that image to be on the right when it's
in a right to left localization.
You have to manually do this.
But to make it a little bit easier
in 10.12 we added a convenience method on button
and an init method which allows you to pass a string, an image,
a target and an action and have the flipping automatically
happen for you.
So that's it for my first section.
I'm going to bring up Jeff to talk about appearances,
storyboards and some other Mac features.
All right, thanks, Corbin.
We got an absolute ton of stuff to cover in this section
so I'm going to dive right in with system appearances.
So here's our app.
We've been looking at it for a while now.
And we're trying to develop our UI a little bit.
And we've decided that we want
to adopt this really slick dark look.
This is kind of characteristic of Pro apps usually.
And we're nothing if not ambitious
with our little demo app here.
But look at this, this looks really complicated.
We have an entirely different system, Window Chrome,
we need all our control artwork to change,
segmented controls, buttons, sliders.
And all of our text labels need to invert
from dark text to light text.
Which seems like an awful lot of work, but actually we can do it
in one line of code using NSAppearance.
All we've got to do is create one of our system appearances,
assign it to the window,
and it's automatically applied to everything within.
You can think of an appearance as sort of a pallet of colors
and artwork that we use to resolve how to draw all
of our standard system controls
and also all of our named colors.
These are things like label color, control color.
And for that reason it's really important
that you use these colors when applicable.
Not only do you fit in better with the entire system theme,
but you'll continue to fit in if that theme changes in the future
or if you change your appearance in the future.
So let's take a closer look.
Here we've got a panel.
It's got tons of nice labels, controls, etcetera, on it.
And all we have to do is just apply dark appearance,
and we see a pretty dramatic change.
Our control artwork has changed, and all of our labels have,
of course, inverted from dark to light.
We can actually take it one step further
and apply this cool vibrancy effect.
You may have noticed a slide ago that the appearance
that we applied is called vibrant dark.
That doesn't mean that you're obligated to use vibrancy,
but it does mean that the artwork looks great
in a vibrant context.
And to get this appearance all you need to do is add all
of your controls as a subview of NSVisualEffect View.
And you'll automatically get this great behind window
blurring and also this cool blend effect
for everything on top.
Now, you might be thinking that's great and all,
but my designer has this really cool like specific color
that they want for our text labels,
and so I'm not going to use label color.
I think that I'm not planning on changing from dark to light
or vice versa so I'm fine, right, I'm safe.
Well, let's have some food for thought.
Let's check in with the accessibility pane,
and we're going to turn on this setting
in the middle here called Increase contrast.
And let's see what that does to our UI.
On the left we again have our standard panel.
But on the right we have that panel
with Increase contrast turned on.
And although we haven't inverted all of our colors,
this transformation is every bit as dramatic
as the transformation from light to dark.
You can see that the window background color has gotten
lighter, our text is darker at every level,
and all of our controls have gotten this really nice,
It makes it really pop against the background.
Now, when you provide a hardcoded color value we can't
really second guess that.
We can't adjust for settings like this.
And so if you don't supply things like label color,
secondary label color, you might be doing a real disservice
to people who need settings like increased contrast
to get the most out of their Mac.
Now I hinted a moment ago at visual effect view and vibrancy.
We're not going to go into depth on that API right now,
but I would refer you back to our talk
in 2014 that's Adopting the Advanced Features
of the new UI of OS X Yosemite.
And that's appearances.
It's a real simple way to theme your application while remaining
harmonious with the overall look of the operating system.
Next up storyboards.
Storyboards are a technology that will allow you
to design not only the individual components
and the views that comprise them for your application,
but also visually design the relationships
between those components.
In this case when I say component storyboards operate
in terms of controllers, there's our window controllers
and view controllers.
And we connect them together with these things called segues,
those are the arrows up there on my screenshot.
And segues abstract away all of the glue code
of putting these components together.
Those are things like adding subviews, adding constraints,
creating popovers, really just housekeeping.
Now, one thing that we need to think about with storyboards is
when we have separable components like these,
you can see I've got a split view here,
and you can see the storyboard that creates it,
all these new little components do their own little thing,
that every piece of UI has some kind of data
that it wants to look at or modify.
And we can't necessarily just drag outlets or actions
from one scene to another.
Of course, if we did that they would not be separable
components anymore, and then we have defeated the purpose
of this whole thing.
But sometimes this data that we're working
on doesn't really live conically in the scene that we have here
in down in our leaf node.
Sometimes it might live all the way
up on the window or in the document.
So how do we propagate that data all the way
up from the top level down into our leaf nodes?
Well, we have a couple of rules of thumb, not hard
and fast rules but just a couple of ideas
that we think are good for handling this.
For one, dependencies should generally cascade downward.
If you're wondering, if you need some kind of rule of thumb
for this just follow the arrows in your segues.
If nothing else this gives you a nice unidirectional information
flow in your application,
and that makes it a lot easier to reason about.
Next try to reduce the amount of assumptions
about your UI structure that you hardcode into your code.
Now, you just got done designing your UI and your storyboard.
And if you hardcode assumptions about how
that structure is put together in your code, now when you want
to make a change to your storyboard you're going back
You've got to change your code, change your storyboard,
and now you're fighting yourself.
Let's not do that.
Let's have our code focus on our data and focus
on designing our interface and interface builder.
And one technique that we can use to address that is
to use protocol conformances
to work really generically across our UI.
So here's an example.
Let's say we have this property here,
it's up on our window controller,
and we want to automatically provide that property
to anything in our view hierarchy that understands it.
So in our didSet we're going to go ahead
and call this propagate method.
And we've got this protocol
that we've defined off screen called photoControllerConsumer.
And this just says I know how to do something
with a photoController.
And so if we look at a child ViewController and see
that it conforms to that protocol we can set
And then we also automatically propagate recursively
to all of its children.
But what about things like popovers or sheet presentations,
things that are kind of on demand?
These probably don't exist
when you're setting a property like that.
And we need to be able to provide their data
on demand before they show up on screen.
And that's exactly what the prepare for segue method is for.
This is called on the presenting view controller
or window controller at the time that the presentation occurs.
And in this case instead of doing something like inspecting
or segue identifier forcibly casting to the controller class
that we expect it to be and then doing some kind
of specific setup, here we're just doing the exact same thing.
We're checking out what protocols it conforms to
and then setting the property appropriately.
And what this does is it changes our logic
from focusing really strictly on identity into capability.
We're saying I have this knowledge,
and anything that's presented off of me could potentially gain
that knowledge automatically through a protocol conformance.
It's really handy.
More about actions.
It's really frequent that the best object
to handle an action that's triggered in UI is not actually
in the same scene that the control is defined in.
And this is exemplified by menu items which are
in their own little scene completely disconnected
from your UI.
And luckily we have a really great mechanism
for handling this case, and that's the responder chain.
If you're coming from a platform like iOS
where maybe you don't use the responder chain quite
so heavily, that is that little orange cube that is in the top
of every storyboard scene.
That's a proxy for the first responder.
And so if you hook up an action from a control
to that proxy it will automatically be sent
up the responder chain when the control is invoked.
But what if there's no object in the responder chain
that handles your method?
Or what if in the case of zoom
in it's not always appropriate to send that action?
For example, we might be at our maximum zoom level,
and so we don't want our zoom in button to just do nothing.
We should be able to look before we leap with our action.
And that's what UI validation is for.
So let's take a look at a block of code
that would inspect UI validation to determine
if a control is actionable.
The first thing that we're going to do is ask NSApp
for the target for an action from a control.
And what that does is it automatically walks the
responder chain and finds some object
that implements that action.
Or it might not find any action,
or it might not find any object at all.
The first case we want to look
at for the results there is any object
that implements the NSUserInterface
And this just means you can ask me proactively
if a control is valid.
And so if you're on the other side of this equation
and you have a control that is conditionally valid you can
implement this method.
And controls like NSMenuItem
and toolbar item will automatically validate
against that method.
So we can ask that method.
And then, of course, we also have the cases of some object
that just handles the method unconditionally or no object
at all which clearly means
that we're not prepared to do that action.
And there's just some techniques for deal with storyboards.
So now we've been talking a lot about the kind
of design time facets of building your app.
Let's look at some more user facing features
that really help you make the most out of our platform.
And the first of those is user activities.
NSUserActivity is this object that describes kind
of what your app is doing right now, what is it viewing,
what is it editing, etcetera.
And this is the object that's used by Handoff
to move those activities between devices.
It kind of takes your whole context and moves it between.
I won't have time to go into the entire API in depth right now,
but I do want to highlight how simple
of an object this really is.
You construct it with an activityType,
and that is a unique identifier
that describes the activity that you're doing.
And this is also a key that you would put into your info plist
to declare I'm a good app to pick
up this activity on another device.
And then you just fill in some basic configuration info
to describe your activity, a nice user facing title
and also some user info.
Basically just the most basic amount of data necessary
to jump back into that task at a later time.
We want to keep this dictionary small
because it's often transmitted wirelessly,
and we definitely want our handoff interaction to be fast.
We also get an opportunity in the delegate
to add the absolute latest information
about our context right before
or at some point before we perform a handoff.
And this is called at some point after you mark an activity
as needing to be saved.
And we'll call this method for you
so you can fill in that latest data.
Now, how do we decide what activity is the
Because, of course, we need
to certainly determine what is being looked at right now.
Well, you can manually manage that with the become current
and resign current methods on NSUserActivity.
That means that you're probably going to have
to follow your user around your application figuring
out what they clicked on and trying to figure
out what they're doing.
AppKit can make this a lot easier by allowing you
to attach activities to the responder chain
for automatic management.
For an example of this let's say we're building a calendar app
which looks a lot like our calendar app.
And we have two activities that we want to put
into our responder chain.
We've got a higher level activity for the view
that we're looking at, this whole, this day view
that we have here, and then we have a much more specific
activity for the specific event
that we want to view right there.
And by attaching these to the responder chain when we click
that event and make it their first responder
that activity becomes the closest thing
in the responder chain through the first responder.
And it becomes current automatically.
Now, you may not have considered Handoff
or the NSUserActivity API before,
maybe you don't have a companion iOS app
or you just haven't really thought
that Handoff is especially compelling for your application
and your specific needs.
But activities are not just for Handoff.
New in Sierra we have Siri on the Mac,
and Siri uses the current activity
to provide context to commands.
So, for example, if you say remind me about this
at some date, Siri is going to infer this
to mean your current activity.
And it will actually even take that activity
and embed it inside of your reminders
so that you can pick it up in the future.
So we see activities as a generalized mechanism
for describing this kind of information.
It's not just for Handoff.
Now, for full information
about the Handoff API I recommend checking out this talk
from 2014, Adopting Handoff in OS X and iOS.
That covers a lot more of the advanced topics
like continuation streams which allow you to move a lot
of data between devices.
The next feature, resume,
which we sometimes call state restoration.
It's one of the best features on the Mac is
that when you quit an application
and then relaunch it, it comes back exactly the way you
In fact, that happens if you crash an application
or even reboot your machine.
Everything just comes back exactly the way that it was.
Now, clearly to accomplish this effect we need to save all
of the UI state that builds up over time and then restore it
when the app relaunches.
But clearly we don't want to save that state in our model.
It doesn't really belong there.
So a state restoration API gives you a distinct place to save
that UI state separately, and it gives you a good place
to restore that state when you're launching
but before your UI has gone up on screen.
You enable it on a per window basis.
It's pretty simple.
You just say isRestorable is true.
And then you may provide a restoration class
that just handles the act of creating your windows
from the encoded data.
And good news if you're using NSDocument,
NSDocument handles this all for you.
Now, what kind of state might we want to restore?
Well, we might choose to save the current tool
that we have active in our app.
We might also want to save the state of the sidebar,
what's selected, what our scroll offset is.
How do we do that?
Well, if you've used NSCoding before it's really,
The first method encodeRestorableState
You implement this on any NSResponder method,
and then it's just like using NSCoding
but for your controllers effectively.
Another important call is invalidateRestorableState.
And this just says whatever my backing data is
for encodeFestorableState has changed in some way.
And we'll schedule to make sure that we save
that state again sometime in the future.
And then finally restoring is just as easy.
It's exactly like you'd expect.
It's a lot like a init with coder.
All you have to do is call super, decode all the saved data
that you've encoded before, and then set up your UI based
on that information that you've encoded.
Now, that's pretty easy,
but we can actually make it even easier.
All you have to do is implement a class method
Of course, we're going to ask super
because that's the polite thing to do,
but then we also append our own key paths.
And these are the properties that you want
to have automatically restored or saved
and restored by the system.
These properties need to be KVC,
that's key value coding accessible
because we access them by key path, and they also need
to be observable so that we can observe them and invalidate
or state when they change.
And that's state restoration.
The third and final technology we want to talk about,
documents in the cloud.
So once upon a time to opt into documents
in the cloud you actually had to proactively opt in
and create a container.
But these days with iCloud Drive
and now especially now iCloud Desktop
and Documents it's more likely than ever
that your app is working with documents that live in iCloud.
Now, this is important because new in 10.12 local copies
of documents might be evicted to free up space.
And this means that you might be working with documents
that aren't actually on the local hard disk.
So how do we handle this?
That seems pretty scary.
Luckily, first off if you're using NSDocument it handles
everything for you so you're in great shape.
But if not you need to make sure
that you're using file coordination.
If you register yourself as a file presenter
with the file coordination API we will make sure
that your document is not evicted out from under you
which is a good thing.
And then next if you use file coordination
to coordination your IO on those files we'll make sure
that we schedule your IO conveniently
after the entire file has been downloaded.
And that's documents in the cloud.
Now, there are a couple of technologies
that we didn't have time to talk about but I do want
to give an honorable mention to.
The first is asset catalogs, faster and smaller
than having loose assets in your bundle.
And they can also help you with things
like wide gamut and right to left.
Accessibility, extremely important.
Cocoa puts really powerful accessibility technologies
within an arm's reach of your application.
And it's extremely important that you make sure
that you learn to use voice over, learn to use all
of these accessible technologies and make sure
that your app is doing the right thing.
You would never ship an app with a visibly broken user interface,
so don't ship an app that has a UI that's broken
Sandboxing and also XPC services, two different
but somewhat related technologies
in that they help you isolate code from the rest of the system
and from other processes.
Sandboxing is, of course, mandatory for the Mac App Store,
but it's appropriate for every app really.
And XPC services can help you separate out code
into separate processes.
This is really great for things like, say,
code that's handling untrusted data off the network
or doing some kind of parsing work.
We all like to think that our code is perfect but, you know,
we also wear seatbelts in our cars, and we're very happy
that it's there if something goes wrong.
So we have covered an absolute ton of content
in a very short amount of time.
So I want to rewind and recap and make sure
that we all remember what we just talked about.
And we started off by looking at creating a modern look
with our modern view and window pipeline making sure you get
great animation performance.
Then we talked about drag and drop and event tracking,
making sure that you get the really cool drag flocking
effect, modern drag file promises.
Then we covered container views likes scroll view and table view
and making sure that we localize them correctly.
Then we walked into system appearances, storyboards
and also a couple of modern Mac features
that really make the platform shine
and really take advantage of it.
Here's the permalink for our talk.
You should find related resources there including the
download for our demo application as soon
as it becomes available.
All of our related sessions are in the past.
I hope you were able to attend them.
If not, then you should definitely check
out the videos online.
And that's a wrap.
Thank you for attending.
Looking for something specific? Enter a topic above and jump straight to the good stuff.
An error occurred when submitting your query. Please check your Internet connection and try again.