WWDC2019 Session 235

Transcript

[ Music ]
>> Good afternoon.
[ Applause ]
So, my name is Jamie
Montgomerie.
I work in the UIKit team.
I'm going to be joined by
colleagues Glen, Nils and Chris.
Together, we're going to tell
you about taking iPad apps on
the Mac to the next level.
In our first talk Introducing
iPad Apps for the Mac, we
covered the basics.
We showed you how to get your
app building and discuss some of
the key API differences that you
should be aware of.
In this talk, we're going to
show how to go further and make
your app better for Mac, cover
multiplatform differences, talk
a little about some design
considerations, talk about how
the application life cycle
differs for Mac and discuss how
you can distribute your app.
So let's talk about making your
UIKit app a better Mac app.
The first thing to remember is
that better iPad apps are better
Mac apps.
So we'll talk about some of the
things you can do to make your
app better on iPad and on Mac.
On iPad, your app runs in
everything, from Slide Over in
iPad mini to our beautiful
12.9-inch iPad Pros.
But now running on Mac, that's
not all.
Your app could be running full
screen on a 27-inch iMac, and
that's with a 77% skill factor.
In fact, the 27-inch display is
more like a 35-inch iPad.
So you can fit a lot in that
window.
So use best practices, support
dynamic type and use Auto
Layout, and lastly, remember
that on the Mac app resize fast.
Can your layout code run at 60
frames per second?
If not, you should check it out
using our Instruments
performance tool.
Next, implement great keyboard
support.
All Macs have a keyboard and Mac
users' hands are always near
them.
It's increasingly common for
iPads to have keyboards too.
You can use our UIKeyCommand
class to respond to keyboard
shortcuts.
You can respond to standard
responder actions in your view
controllers, things like the
cut, copy and paste methods.
And look after your responder
ping.
Set first responder so that key
commands go to the right places.
And this will help at iPad too.
If your app is a game, consider
supporting game controllers that
will make the experience better
for your iOS and your Mac users.
So a couple of years ago, we
introduced some great drag and
drop APIs in UIKit.
IPad users now expect drag and
drop, and Mac users have
expected it for a long time.
Making great use of UI drag
interaction and UI drop
interaction will make your Mac
and your iPad users happy.
Now this one is always good
advice.
Use the latest APIs.
If you haven't done it for a
while, now is the time to
examine your app and look at
your use of deprecated APIs.
I said it's always good advice
but it's even better advice this
year as we discussed in our
introductory talk many of the
deprecated iOS APIs are simply
not available on Mac.
So use WKWebView not UIWebView,
use Metal not OpenGL ES.
We've been working hard on these
modern alternatives and using
them enables you to take
advantage of all that work in
iPad and on Mac.
Lastly, support our new iOS 13
features.
For example, if you support
multiple windows in your iPad
app, every UI window scene will
be a window on Mac.
Support Dark Mode on iPad and
you'll support Dark Mode on Mac
too.
I could call it some specific
talks here but there are a lot
this year.
So go and have a browse and
watch the ones that you're
interested in, remembering
better iPad apps make better Mac
apps.
So, better iPad apps or better
Mac apps but better Mac apps
also contain refinements just
for Mac.
So we'll take a look about what
you can do to make your UIKit
app feel even more at home on
Mac.
There are a whole range of
things here.
They spend the gamut from making
more use of existing UIKit APIs
to enable unique Mac features to
some APIs that are completely
new on Mac, or at least
completely new to UIKit
developers.
We're going to take a quick look
at the breadth of APIs available
to you now.
So what's the first thing that
you think of that's unique to
Mac?
For me, it's the Menu Bar.
Global Menu Bars are unique to
Mac.
They store all the possible
actions your user can take in
one place and neatly tucked away
at the top of the screen.
They're global to the app and
the items in them are enabled or
disabled based on what the user
is doing.
So for your iPad app on Mac, you
can just accept the default Menu
Bar.
No extra work is required.
You can also compose a Menu Bar
in Interface Builder as we saw
in our first session.
Or for field control, you can
customize in code.
So I spoke about UIKeyCommand
earlier.
If you're a sharp-eyed API dev
reader, you'll have noticed that
this year has a new UI command
super class.
And this along with UIMenu and
UIMenuBuilder enable you to take
full control over the Mac Menu
Bar.
We're going to see a demo of
this in a moment.
One neat thing about this API is
that UIKeyCommands that you use
in Menus will be available in
iPad too.
They'll be shown in the
discovery builder you get when
you hold on the Command key.
So if you use this API to make
your app a better Mac app,
you'll be making it a better
iPad app too.
Now, there's another kind of
menu in Mac, the Context Menu.
It's shown in the Control click
or right click.
And unlike the Main Menu, its
contents are dynamic.
They're based on what's under
the mouse pointer.
We have a great new UIKit API
for this available on iOS 2,
UIContextMenuInteraction.
It uses UICommands and its
sibling, UIAction.
UIAction is block base, so maybe
more suitable for this kind of
menu.
And this cross platform API
works uniquely on Mac showing
the context menu that we're all
used to.
To learn more about this API,
you should check out our
Modernizing Your UI for iOS 13
session.
So we've talked about some new
UIKit APIs.
Here's an old UIKit API,
UISplitViewController.
So the familiar sidebars on Mac
are very similar to Split Views
in iOS.
They're often used for master
detail kinds of UI.
On Mac, UISplitViewController
manage views are automatically
drag resizable from the minimum
to maximum column width.
And you can use our new
primaryBackgroundStyle property
to have the left-hand column
take on a Mac sidebar look.
When you're using the sidebar
background style, embedded table
views using either of our group
styles will take on a source
list like appearance and look
right at home on Mac.
We're going to see this in a
second in the demo too.
Hover, it doesn't exist on iPad
but it's an instantly familiar
idea to Mac users.
We've added a new easy to use
gesture recognizer,
UIHoverGestureRecognizer.
It allows you to use Hover in
you UIKit apps.
On screen here, you can see our
stocks app making great use of
this to display prices as the
user moves the most plunger over
the price chart.
Now for some things that differ
completely on Mac, we already
have some great Mac APIs.
One example of this is the
Toolbar.
Mac apps often have Toolbars at
the top of the window.
Because this is a Mac only
feature, we've exposed the
regular Mac only API, NSToolbar.
You can get to it via your
UIWindowScene's titlebar
property.
There's a demo of this coming up
soon too.
Another kind of bar unique to
Mac is the Touch Bar.
Touch Bars are on Mac harder
feature.
On MacBook Pros, they sit just
above the keyboard, unlike
context menus, what's in them
changes based on the what the
user is doing.
To allow you to support Touch
Bar, we've exposed the existing
NSTouchBar class to UIKit apps.
And it's available via new
UIResponder and UIViewController
APIs.
So there are of course the whole
host of other Mac features you
could adopt.
You can have some control over
window sizing, you could use the
iOS printing APIs to implement
print support on Mac, you can
author a Help Book which will
give your users help accessible
directly from the Help Menu, and
you can customize your Assets
And Strings to make how your app
looks or what your app says
unique to Mac.
Now, last but not the least, I'd
like to give a mention to the
app icon.
If you do nothing, your icon for
the iPad app will be used for
your Mac app.
But Mac style differs from iPad
style so you can also create an
icon for your app that's just
for Mac.
This may seem pretty trivial but
it's the first thing that your
users see.
So I encourage you to put some
time and effort into it.
Now, I'd like to introduce my
colleague Glen.
He'll show you firsthand how to
make an iPad app into a better
Mac app.
[Applause]
>> Thanks, Jaime.
As my colleague had said, better
iPad apps make for better Mac
apps.
Let's take an example of this.
So here, we have an app called
ChocolateChip.
It's an iPad app that shows
yummy recipes.
In previous demos, all we did
was-- OK, let's and wait for it
to load.
In previous demos, we just
checked the Mac-- checkbox here,
fix any build issues and just
built and ran the app like so.
OK. Just taking a while.
As you can see, we have a
brand-new Mac app with all the
niceties you expect for a Mac
app.
For example, all your content
appears in a window that you can
drag around.
And if you have supported
varying screen sizes and
optimize drawing like Jamie
suggested, you will also be able
to get resizing windows fast and
smooth, like so.
Now, if you will implement it,
the existing move callback in
the table view controller, you
can also reorder rows in the
table view controller like so
without going through a separate
editing mode.
So this is a Mac feature.
Next, we've used the new
UIContextMenuInteraction API
which we introduced this year,
configure it with the right
UIActions and UICommands, then
your context menus carry over to
the Mac seamlessly.
So here I've got Add to
Favorites.
I can make Donuts my favorites
or I can take that away as well.
Now talking about menus.
You also get a default menu bar
that start with all the default
items that you're used to.
For example, cut, copy, past,
undo and redo.
And these items, menu items are
enabled or disabled depending on
what's on the responder chain.
Finally, if you use the new Dark
Mode API in iOS and specify your
colors using these system
colors, then Dark Mode works
really well as well.
As you can see, everything goes
dark and all your colors are set
and your highlights are set
correctly as well.
And all of this without a single
line of code.
[ Applause ]
Yeah. Thank you.
But what if you want it to
catalyze your app to the next
level?
If you want it to make it look
and feel like it really belongs
in a Mac, what can we do?
Well, let's fix three things
today, sidebars, toolbars and
menu bars.
So it looks like I'll be your
friendly bartender for today.
Yeah, thanks [laughter].
OK. First order of business is
you look at this master view
here.
This is a master view of the
regular Split View controller.
But it really should look like a
Mac toolbar.
So let's set-- Let's figure out
how to set the toolbar style.
First, we go to the
RecipeSplitViewController and we
need to hook on to the
viewDidLoad delegate call.
And here, our call has to be
conditionalized for UIKit for
Mac so that it only applies for
Mac builds.
And we only need to insert a
primaryBackgroundStyle and a
new, you know, enumeration of
sidebar.
So with this single line of
code, we now have a-- and we
have a quick rebuild.
We now have the sidebar style.
So just to show you how it looks
like and to convince you it
really does look like a sidebar.
You can see it has the
translucency of a sidebar as I
move it around.
The other things about sidebars
are you can also set in the
general preferences the sidebar
icon size.
So here I'm setting it to small.
And as you can see, it
dynamically updates.
You can also set it to large.
I will leave it at medium.
And as before, reordering what's
-- just as what I did before.
OK. Sorry.
Back to the app.
Next, we should really put in a
toolbar.
We have various widgets
integrated into the title bar
chrome so that users can click
on their commonly used commands.
Now, this is different from iOS
where toolbars are typically
found at the bottom of the app.
We'll have a widget to filter
the recipes.
We'll also have a widget to add
a new recipe.
So let's see how to do that.
We'll go over to the scene
delegate.
And here in the scene delegate,
we're hooking on to the scene
willConnect callback.
And again, we also have to
conditionalize this for
UIKitForMac so that it only
applies in the Mac.
We're digging into the object
model of the scene-- windowScene
here.
And we grab this object called
the titlebar.
The titlebar allows us to set
toolbars as well as change title
bar visibility.
So let's create a new toolbar.
So, notice now that I'm using NS
prefix.
This is actually AppKit object.
And I need to give it identifier
which I have one previously set.
And all I need to do now is a
title bar.
I want to set the toolbar to the
toolbar I just created.
So with these two lines of code,
I've now created a toolbar
that's attached to the title
bar.
But this code is not
particularly interesting, or the
result is not particularly
interesting because we actually
need to configure the toolbar of
the actual items.
So let's see how to do that.
I'll leave you to assign this
code.
First, we're grabbing-- we need
to grab the rootViewController,
and then we pass it to an
initializer of the
ToolbarDelegate which is a class
we've created.
And we set up the delegate.
We want to allow user
customization of the toolbar.
We also want to center the
navigationItem which is the
filter that I talked about.
And finally, I want to set the
titleVisibility to hidden so
that the title doesn't overlap
the navigation item that I've
just created.
So, with a quick recompile and
build, we now have a toolbar.
And you can see the toolbar has
filters for various meals.
So, you know, for breakfast I
think I'm having donuts.
For lunch, I'm having pizzas.
For dinner, I'm having
spaghetti.
Hopefully for desert I'm not
having spaghetti again.
Wow, it's chocolate chip
cookies.
Yes. And we also have a plus
widget or the add item widget
which allows us to add new
recipes.
OK. Finally, we should actually
also customize the menu bar so
that users can find all the
commands they want in a
convenient place.
Now, menu bars are great places
to put all the commands you want
and the system will enable or
disable them accordingly, in
contrast to toolbars where you
might only put commonly used
commands.
In the introduction session, we
showed you how to do this by
dragging out a menu bar in Xcode
Interface Builder.
But sometimes you want to better
control your menus at runtime.
So I'll show you how to do this
in code.
What are we going to do?
We are going to remove the
Format menu because having a
Format menu doesn't make a lot
of sense because this app has no
editable text.
So not a lot of sense to have a
font menu or even text
alignment.
We also want to add two items to
the File menu.
So here, I want to add a new
recipe command which is similar
to the add recipe widget here.
And we also want to add a
command to add or remove
favorite status which is similar
to the context menu here.
All right.
Let's see how to do that.
We'll go over to the app
delegate.
And here in the app delegate, we
need to override a single
method.
And this method is buildCommands
with builder, and it gets passed
a UICommandBuilder.
In subsequent seeds, this will
be called buildMenu and
buildMenu with builder and that
will be passed a UIMenuBuilder.
So be aware of that if you are
working with the current seed.
First thing you need to do is to
make sure that we're actually
building for the Main menu.
So here we're guarding against
checking to see that the system
is the main system.
Sometimes you'll get a .context
if you're building for the-- a
context menu.
With that, we need to talk to
the builder.
So builder, I want to remove a
particular menu.
So here, this menu that I wanted
to remove is a Format menu.
So as you can see, code
completion helps you out a lot
and suggests what kinds of menus
you can actually remove.
So quick build and run.
You can see we've removed the
Format menu from between the
Edit and View.
So, let's go back to this.
So the builder is actually
pretty flexible.
You can do anything from
replacing an entire menu
structure to making selective
edits like what I'm doing.
OK. So next thing I want to do
is I actually want to insert the
content.
So let's do a talk to the
builder and say insert a child
at the start here of the File
menu.
So, what about this content?
I have a little commands here
that will be added.
The first one is
newRecipeCommand.
And this is a key command which
is hooked on to the createRecipe
method.
And it's a key command so it
will take a input of f and a
command and option.
So if you press Command option
f, you'll also be able to invoke
this UIKeyCommand.
The nice thing about having a
UIKeyCommand here is this is
perfectly cross platform.
This will also appear, you know,
discoverability view or
discoverability had that you get
on the iPad.
So without conditionalizing any
code.
This works on both Mac as well
as iOS.
Next, we can create a
makeFavoriteCommand.
And this is just a regular
command without any key
equivalence.
And this goes to toggleSelected
RecipeFavoriteState.
And finally, we make a menu
which is a construct that groups
both the new command--
newRecipeCommand and the
makeFavoriteCommand.
One thing to highlight here is
we're passing the options of
displayInline so that the menu
will be displayed-- the contents
in the menu will be displayed
together with the parent.
If you omitted these options,
what will happen is the menu
will be displayed as a
hierarchical menu which is what
you might be used to on the Mac.
OK. Let's see what this looks
like here.
So we insert this into the-- at
the beginning of File menu.
OK. We have a quick rebuild and
run.
As you can see, it now has a New
Recipe command which does the
same thing as that plus widget.
It also has a Make Favorite
which does-- it gives a love
heart on the chocolate chips.
And we can remove favorites as
well.
In summary, what I've shown you
is how much you get for free
just by rebuilding your iPad app
on the Mac.
I've also shown you sidebars,
toolbars and menu bars to take
you all-- to catalyze your app
to the next level.
So it's over to you, Jamie, for
some design considerations.
[ Applause ]
>> Thanks, Glen.
So, now we've talked about some
of the technical details of
adapting your iPad app for Mac,
let's take a short dip into some
design considerations.
First, Navigation.
Think about how your users find
their way around your app.
If you're not already think
about using a sidebar on Mac and
maybe a Split View on iPad too,
and reconsider your use of tab
bars.
On the Mac, you should consider
using a segmented control in the
toolbar instead.
Next, layout.
We've talked about screen sizes
already.
It should be flexible in your
layout and take advantage of the
big window.
You can reflow and redesign your
user interface and you can sue
custom assets to reword hardware
related content or just to take
advantage of the space.
iPad design is larger than Mac
design.
It's optimized for touch.
So baseline font sizes are not
the same.
On iOS, most text is 17 points
and on Mac it's 13 points.
So we scale UIKit content for
you by 77% compared to how it
would appear in an AppKit app.
This means that UIKit points are
smaller than AppKit points on
screen.
Even bitmap graphics in UIKit
apps will be shown at 77% of the
size that they would be in an
AppKit app.
Now because the scaling is
global to the app, in most
cases, you don't need to worry
about it.
If you do want some more direct
control especially over font
scaling, you should look at the
Font Management and Text Scaling
session to learn all about it.
And menu bars.
We talked about the how already,
but what should you put in it?
So I mentioned that the menu
contains all possible actions in
one place that is global to the
app and that items are enabled
or disabled based on what the
user is doing.
This means that what's in the
menus shouldn't change.
You should build it only once at
launch time.
And you really should think
about all the functions your app
has and make sure they're all
available via the menu bar.
And we just talked about how the
iPad is optimized for touch.
One thing that's unique to iPad
is direct multitouch.
The Mac doesn't have this but it
does always have a keyboard and
the mouse or trackpad.
You should think about how you
can map the gestures used in
your app to these input devices,
reimplementing them for mouse
and trackpad on Mac.
And when you're doing this,
remember to consider
accessibility.
Now that really was just a short
dip into design topics.
There's lots of useful
information on device in the Mac
Human Interface Guidelines.
You should look at it while
you're designing your app.
And I encourage you to check out
the Design for iPad Apps on Mac
talk.
It contains lots of great advice
on how to make your UIKit app a
great Mac app.
So now we've talked about making
your iPad app into a better Mac
app, I'd like to introduce Nils
who'll talk about another thing
that's unique to UIKit apps on
Mac, the application lifecycle.
[ Applause ]
>> Thank you Jamie.
Good afternoon, everyone.
My name is Nils Beck.
I'm an engineer with the AppKit
framework team and I'm excited
to talk to you today about the
application lifecycle of your
iPad app for the Mac.
When we compare the app
lifecycle on iOS to that on
macOS, we find that there are
differences.
In some iOS, specific behaviors
are reflected in the UIKit API.
This presents a challenge when
running iPad apps on the Mac.
How do we map macOS app states
to the iOS API?
Let's start by first reviewing
the app lifecycle states as they
exist on iOS.
You can follow along in the
chart that I'll be showing here
on the right-hand side.
When your app is on screen and
the person is normally
interacting with your app on
iPad, it is both in the
foreground and active.
Your app is inactive but still
in the foreground when something
is occluding it and it is
therefore not receiving events.
This is usually temporary, for
example when control center is
visible on top of your app.
Once your app is in the
background, the user is not
interacting with it.
For example, they may have used
the task switcher and swiped
sideways to a different app, but
you may still perform background
task completion and your app is
suspended when no background
tasks remain.
Once suspended, your app is
frozen and gets no more CPU
cycles.
At this point, it may be killed
by the system at any moment
without further notification.
And finally, your app is not
running when it is no longer
even in memory.
Putting all of this together,
let's look at the possible state
changes you will see whether
running an iOS or a macOS.
I will walk you through these in
a second but first I'd like to
note that to keep simple--
things simple for you, iPad apps
on the Mac will get the same
delegate calls and notifications
as an iOS.
You may be used to handling
these state changes in app
delegate methods, but they also
existed and NSNotification shown
here.
We recommend using these
notifications as they work even
when you are opted into the new
multi-window API where some app
delegate calls are omitted.
It's the same on iOS by the way.
Also, the overall app state in a
multi-window app depends on the
activation states of the
individual scenes.
So you will likely want to
handle state changes per scene.
But in this talk, we'll just
focus on the overall app
lifecycle.
In addition to the delegate
calls and notifications, on
macOS, the sequences of these
state changes are also the same
as an iPad.
As I promised a second ago,
let's take a closer look at
these sequences of state change
notifications.
On the launch, you will see
didFinishLaunching and
didBecomeActive.
On the way out, you will see
willResignActive and
didEnterBackground.
You may also get willTerminate
if we are certain that the app
is exiting and your app is not
already suspended.
And finally, on the way back in
from the background state, you
will see willEnterForeground and
didBecomeActive.
While the notifications and
sequences are the same on both
platforms, we did alter when
these transitions occur on
macOS.
We'll get back to that in a
moment.
Now, back in iOS, we recommend
that your code should respond to
these state changes in certain
ways.
For example, when you learn
about the deactivation of your
app through
willResignActiveNotification,
you're expected to among other
things, reduce the frame rate of
on-screen content, stop various
types of nonessential work, for
example, timers, queues and
queries, and there may also be
other side effects.
For example, you may want to
pause game play in your game.
Similarly, when your app enters
the background state through
didEnterBackgroundNotification,
on iOS, your app is expected to
cease all rendering, reduce your
CPU usage to the bare minimum
necessary, and free up as much
memory as possible.
Again, there may also be other
side effects.
For example, if the content
shown on screen should be
private, you may want to obscure
it before the screenshot is
taken.
Now, we don't want you to have
to change all of the state
change handling code when
bringing your iPad app to macOS.
But your iPad app running on
macOS is now a Mac app.
How does that change things?
Here are some Mac specific
considerations.
It is common for many Mac apps
to share the screen at the same
time.
One of those applications is
Frontmost which just means that
it receives key events and its
menu bar is shown.
Many things may affect the
visibility of an app's contents.
For example, a window may be
occluded by another window, or
the window may be minimized to
the dock, or it could even be in
another space entirely.
It is also possible that the app
isn't showing contents on screen
for other reasons.
For example, the whole app could
be hidden, or it might just not
have any windows right now, or
it's even possible that your app
could be running in an
off-screen log in session that
only exists on a virtual screen
which you might be accessing via
VNC during one of those late
night debugging sessions.
Regardless of their frontmost
state and content feasibility,
Mac apps are generally not
expected to change their
behavior as drastically as iOS
apps.
All running apps are expected to
be doing valuable work for the
user.
Also, you want apps that are not
frontmost to still receive
scroll events, click-throughs
and hover events.
And a Mac user would not
necessarily expect game play to
pause when they switch to a
different app, or rendering to
be disabled entirely while
trying to use your app through
one of those VNC sessions on a
virtual screen.
I told you earlier that we had
altered when these state changes
occur compared to iOS.
And here it is.
Based on what I just told you,
we have decided that on macOS,
we will simultaneously keep all
iPad apps foreground and active
almost all the time.
We will only enter the
foreground inactive and
background states during app
termination and background
launches.
In other words, as long as the
user perceives your app to be
running, it is foreground active
to avoid any unMac like side
effects.
Let me repeat that.
The UIKit state change
notifications are not going to
be called for example when your
app gains or loses the menu bar,
or a window becomes occluded.
That means that your app won't
reduce its resource consumption
as eagerly as on iOS.
However, the AppNap heuristics
are applied to all Mac apps
including your iPad app.
AppNap is a feature of macOS
where the system constantly
observes various properties of
the app for indications that it
is not in use.
For example, whether it is
visible, actively drawing,
playing audio and so on.
AppNap then automatically
applies throttling when it is
deemed safe to do so.
As I mentioned earlier, we do
enter the other app states
during termination and
background launches.
Let's start with app
termination.
Something developers need to be
aware of when moving their iPad
apps to macOS is that switching
between apps in iOS has no
single equivalent on macOS.
For example, switching to
another app in iOS while
background audio is still
playing is like changing a
frontmost Mac app to a different
frontmost Mac app, because your
app continues to be perceived as
running by the user.
But when the user doesn't return
to your app on iOS and no audio
was playing, this might be more
similar to terminating a Mac on
macOS because your app is out of
sight and out of mind and exits
eventually.
On macOS, user initiated app
termination of your iPad app
gets the same state transitions
as switching to a different app
in iOS, for example by using the
task switcher.
The state change notifications
will take you from foreground
active through foreground
inactive to the background.
To the user, however, the app
will immediately appear as no
longer running, the app's
remaining windows are made
invisible, it is no longer in
control of the menu bar, there
will be no indicator light in
the dock, and your app is no
longer available in the macOS
command tab task switcher.
But behind the scenes, we give
background tasks a chance to
finish if they are created
through the usual API on
UIApplication that I've shown
here.
Just like in iOS, end your
background tasks quickly or risk
task expiration.
There is one exception to this
rule.
Background audio plist entries
are ignored.
The process will terminate in
spite of background audio
requests.
After all, to the user, the app
already looks terminated.
So your media frameworks will
pause your-- our media
frameworks will pause your
playback for you upon entering
the background state.
However, keep in mind that
background audio is not needed
on macOS.
Your app will remain foreground
active even when the user
switches to a different app.
Additionally, if the user tries
to launch the app while it is
still backgrounded, we simply
return your app to the
foreground active state.
This is similar to returning to
your app in the iOS task
switcher.
This sort of relaunch is only
possible while your app is still
processing background tasks, not
once the synchronous handling of
willTerminate has begun.
So try to minimize this phase
where your app ignores user
attempts to relaunch it.
Once the last background task
completes or expires, the
process exits without
suspension.
As mentioned before, you will
get the same delegate calls and
notifications as on iOS.
In thid case, that means you
will see willResignActive,
didEnterBackground, and
willTerminate because we will
exit the process rather than
just suspending it like on iOS.
So, that was iPad app
termination on Mac.
As I mentioned earlier, the
other way for your iPad app on
Mac to enter the background
state is when it is launched
directly into the background.
iOS has quite a few APIs that
allow you to opt into some form
of background launch for your
app.
To learn much more about this
topic, check out the session
Advances in App Background
Execution, also from this year.
The following subset of these
APIs is supported for your iPad
apps for Mac.
URL sessions configured for
background downloads, silent
remote user notifications with
the content available specified
in the APS dictionary.
You could use this, for example,
to update your app's contents
when a new content arrives
server-side, also when our user
invokes a notification action
that does not specify the
foreground option.
For example, they might like a
post thorugh a user notification
and we could background launch
your app to let the server know
about this.
And finally, we also support the
new BackgroundTasks framework
including the new
BGProcessingTask as well as
BGAppRefreshTask for traditional
background app refresh.
Note that for iPad apps for Mac,
the app refresh feature is only
supported through this new API.
The usual restrictions on
background runtime and process
priority apply same as on iOS.
So if your task takes too much
time, your app risks being
suspended or even killed.
If the user attempts to launch
the app while it is already
running in the background, we
will simply transition it to
foreground active similar to the
previously discussed relaunch
during termination.
For background launches as well,
all state transitions will match
those on iOS.
In this case, that means you
will only see didFinishLaunching
and whatever callback was
specific to the background
launch API that was used.
Only during the full launch by
the user will you additionally
see willEnterForeground and
didBecomeActive.
A word on app suspension.
iPad apps are rarely suspended
on macOS compared to iOS.
During regular termination, the
app will exit exclusively
without suspension.
And during app switching on
macOS, this does not cause
backgrounding or suspension.
But you will likely still see it
occur if an app is launched
directly into the background.
Now on iPad, your app may be
terminated when resources are
needed elsewhere especially if
it's already suspended.
But on macOS, if your iPad app
is suspended, it is always
terminated immediately even if
no memory pressure exists.
Speaking of memory, the Mac
memory model applies which means
there are no enforced memory
limits.
Your app won't risk being killed
while doing heavy lifting.
But this is a double-edged
sword.
Because your app runs longer,
user may notice degraded system
performance from leaks piling
up.
This is specially the case if it
causes VM swapping on hard disk
base systems.
So please, use the allocations
profiling template and
instruments to find and fix your
leaks.
This will also benefit your app
on iPad.
Now, that was a lot of
information, so let's recap.
On macOs, your iPad app will
spend most of its time in the
foreground active state.
App termination and background
launches do allow your app to
get into the foreground inactive
and background states though.
Don't expect audio playback to
continue while your app is
backgrounded even if you
requested it.
And finally, you can just
continue to respond to these
state changes the same way as
you do on iOS.
And now, I'm going to hand it
over to Chris who will talk to
you about distributing your iPad
app to the Mac.
Thank you.
[ Applause ]
>> Thank you, Nils.
You've been building iOS apps
for a while now.
You know how to distribute on
the App Store, how to use
TestFlight, how to distribute
yourself using development
signing, ad hoc or enterprise.
But now, you're going to be
building and selling a second
app on the Mac all from the same
code base.
How are you going to do that?
What will be different about the
Mac?
How will you ensure that your
customers have a seamless
experience using both apps?
That's what we're going to
cover.
Let's go back to the beginning.
You opened up the project editor
in Xcode.
You enabled the Mac support.
You've been working very hard.
You've got your app perfectly
tuned and you think to yourself,
gosh, I haven't had to worry
about code signing once.
Well, that's great.
But when you want to know more
about how signing is working,
you can head over to the Signing
and Capabilities tab.
There you can see there is a lot
going on under the hood to sign
your iOS and Mac app.
Let's take a closer look.
First off, if you enable
automatic signing, all of the
maintenance of code signing is
taken care of by Xcode.
It will make this transition
super easy.
I highly recommend using
automatic signing.
Now, using automatic signing,
Xcode will create a necessary
provisioning profile for the Mac
and one for iOS.
And we've modernized things a
bit.
Instead of using an iOs
certificate and a Mac
certificate, we're going to use
one unified Apple developer
certificate.
So, that's a nice improvement.
[ Applause ]
Now, when you add Mac support,
Xcode will use automatic signing
to register a reserved bundle
identifier for your app, for
your Mac app prefixed with UIKit
for Mac.
Now, this is important.
If you have been making use of
your bundle identifier in your
code or in your entitlements,
you need to think about how that
may affect both apps.
The entitlements file which you
have been using for your iOS app
is shared when signing for the
Mac.
So there's no issue there.
And when you enable Mac support,
Xcode will add those
capabilities that are required
on the Mac, Hardened Runtime and
App Sandbox.
So for instance, if you've
provided a usage description for
the camera in your info.plist,
Xcode will update your
entitlements file so that your
app can make use of the camera
on the Mac.
If you're using an iCloud
default container, that is based
on your bundle identifier.
So, Xcode will adjust your
entitlements file so that your
iOS app and your Mac app point
explicitly to your existing
container.
If you've been using the default
container API, you may continue
doing so.
Now, Xcode will migrate a lot
for you.
But if you want to share
functionality between your iOS
app and your Mac app, there may
be some manual steps that you
need to take.
If you're using the Keychain in
your app, you should add the
Keychain Sharing Capability in
Xcode.
iCloud Keychain is made
available by default on iOS.
But on the Mac, you need to
declare your intent to use it
through this capability.
If you're using push
notifications, continue sending
notifications to your iOS app.
Those notifications will be
received by your iOS and Mac
app.
All right.
That's what you need to know to
get your project set up properly
for development.
Now, when you're ready to share
your app with others, you begin
in Xcode by building an archive.
And on iOS, you use that archive
to export for the store as an
IPA.
But things are a bit different
on the Mac.
When Xcode uploads your app to
the App Store, it sends it as a
Mac package behind the scenes.
Now, uploading to the App Store
is very similar to how it is for
iOS.
Let me show you.
When you have your project open,
you can use the Product menu to
switch your Run Destination to
My Mac.
then you Archive.
The organizer will open up and
show you your brand-new Mac app
archive.
From there, you can click the
Distribute App button and follow
that same upload workflow that
you're used to.
Of course, you can do all of
this from the command line
including uploading from Xcode
build.
For more on how to automate all
of this, I recommend checking
out the session from 2017,
What's New in Signing for Xcode
and Xcode Server.
OK. So you've built your app,
but there are some steps to take
before sending your app to the
App Store.
First, this is a new Mac app, so
you need to create a new app
record in App Store Connect just
like you did for iOS, then
before and now once more.
Now when you do this, you're
going to associate that App
Record with the app identifier
that Xcode synthesized for you,
the one prefix with UIKit for
Mac.
And whenever you upload to the
App Store, you should always
increment your build number.
This is a little different from
iOS.
macOS uses the build number to
determine the order of your
releases.
And if you've done all that, you
can upload from Xcode and
release your app on the App
Store.
Now, a cool thing about the Mac
is that you have more control
over how you install apps.
When a customer downloads your
app from the App Store, it is
perfectly possible to copy that
app to another Mac and run it,
which brings up a few things.
App Thinning isn't used on
macOS.
Your app will always contain the
complete set of resources
necessary to run on any Mac.
And because your app can be
dragged and dropped to any Mac,
it is up to you to add receipt
validation logic to your app.
So whenever your app launches,
it should be checking that it
was legitimately purchased and
is allowed to run.
For more on how to add receipt
validation to your app, I
recommend checking out the
Advanced StoreKit talk from
2017.
The good news is that once your
app is on the App Store, you
have access to the same great
tools, like App Store Connect,
App Analytics to track your
sales and marketing efforts, and
the Xcode Crashes Organizer to
triage any crashes that may be
affecting your customers.
Your app can use the same
StoreKit and GameKit APIs.
Now, if you're using those
API's, there is some additional
set up required on App Store
Connect.
If you're using In-App Purchases
or subscriptions, you will need
to recreate those in App Store
Connect for your new Mac app.
And if you want to share the
same purchase history between
both apps, that will require
that you maintain your own
server-side synching solution.
If you have a game, you should
begin using Game Center Groups
to share your leaderboard and
achievement data between both
apps.
And if you have a multiplayer
game, you should update the
Multiplayer Compatibility
section in App Store Connect
that will ensure that your
players are matched with the
appropriate versions of your app
across iOS and Mac.
All right.
So you can begin playing with
all of this now, but a heads-up.
For the time being, the App
Store will not be accepting
uploads with the prefix UIKit
for Mac.
You can still create an App
record and experiment with those
StoreKit and GameKit APIs in the
meantime.
Over the summer, we will begin
accepting uploads and this would
be a good time to practice
uploading your app.
And finally, when Xcode 11 is
released, you can begin
submitting for app review.
And that is distribution on the
App Store.
[Applause]
Now, there are other options if
you'd like to distribute outside
of the App Store.
You can serve you app directly
to your customers if you sign
your app with a developer ID
certificate and get it notarized
by Apple.
Notarized apps are a great
evolution and security for the
Mac.
We can all take comfort that the
apps that we download and run on
our Mac have been signed by the
developer that wrote it and that
signature has been vetted by
Apple.
There's no app review for these
apps, but we enforce good
policies like requiring that
Hardened Runtime be turned on in
your app.
And there's great command line
tool support to sign and
notarize your app.
And sharing a notarized app with
your team is a great alternative
to TestFlight which is not
available on the Mac.
One caveat though, signing this
way doesn't allow you to use
those App Store based features
like GameKit and StoreKit.
Now, signing a notarized app is
not as scary as it sounds.
You can do all of this from
Xcode.
The first step is uploading your
app to Apple.
In this process, Xcode will sign
your app with the developer ID
certificate and Apple will scan
it for malware.
After a few minutes, Xcode will
receive a notification, notarize
the app, at which point you can
export it, you can zip it up or
put it in a disk image and make
it available on your server.
If you want to know more about
the security model or how to use
it in your app, I recommend
checking out the session from
this year, All About
Notarization.
All right.
Those are your options for
distribution to your customers.
There is a third distribution
option worth mentioning and that
is development signing.
Now, development signing is just
the same as you're used to on
iOS.
It is great for sharing a build
with your team to test.
And remember, when you share
with a new Mac, remember to
register any new devices on the
developer portal.
And development signing is what
you should use to test those
StoreKit and GameKit APIs.
You can-- When you use
development signing, you can use
Sandbox test accounts instead of
spending your own money
exercising receipt validation.
And those are your options for
distribution on the Mac.
Now, to be sure, this has all
been a lot to take in.
But consider if you will how far
we've come.
Maybe you're like me.
You began as an iPhone
developer.
You mastered table views and
screen rotation, then you
graduated to something a bit
bigger.
You sought opportunity on iPad.
You challenged yourself with a
new form factor adding
multitasking and drag and drop.
Your next adventure will be the
Mac.
You'll conquer window resizing
and menu bars.
I am delighted to see what you
bring to the Mac.
We all stand to share greatly in
its growth.
Now, go check that checkbox.
Please, join us in the labs.
[Applause]
Please watch the related
sessions.
Please, have a fantastic WWDC.
Thank you.
[ Applause ]