WWDC2015 Session 106

Transcript

X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
>> CHRIS LATTNER: Welcome
to What's New in Swift.
[ Applause ]
I'm Chris Lattner.
I'll start off this talk
and my colleague John
McCall will take you
through the second half to
take you through what's new.
Before we get going, I
thought it would be interesting
or helpful to look at what
we're trying to do here.
What are the goals and what are
the philosophies behind Swift 2?
We had three big things
we were going for.
First, fundamentals.
We want the core features
and the core behavior
of the language and
the tools to be great.
A lot of this is taking a
look at the feedback that many
of you have produced in
the process of using Swift.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
of you have produced in
the process of using Swift.
So a lot of what this is is --
I want to thank you for
all the feedback you have.
You guys are continuing
to shape Swift
through all the great feedback
the producers have that use it.
Second up is safety.
Safety is a core value of Swift.
We really want it to be easy
to write safe code by default,
and we think that
the new availability
in error handling constructs
will be a great new way
to do this.
Third, beauty.
We want your code
to be beautiful.
As programmers, we work
with code all the time.
This is quite important to us.
We have added new things
to Swift making it easier
to write more beautiful
and natural code.
Today we'll talk about
five new things in Swift.
Before we get diving into
what's new in Swift 2,
I think it is important to point
out that Swift 1.2 was
also a huge update.
It was released just
three months ago.
Because of time limitations,
we don't have time
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Because of time limitations,
we don't have time
to talk about it much.
But if you're interested and
you haven't seen it already,
check out the Swift
programming language book
and the Xcode 6 release notes.
Let's dive in and talk
about fundamentals.
Fundamentals is about
refining the core behavior
of the language and
how it works together.
So there are a lot of
little things here.
This will feel like a bit of a
random walk, but stick with me.
We'll start off with enums.
Enums are one of
Swift's best features.
Here I have an enum enumerating
some common household pets.
Enums are great because they're
simple to define and use.
On the other hand, if you have
played with them in a playground
or printed one out, you may
have been left wanting for more.
In Swift 2, enums now carry
enough reflection information
that you can print them,
and they work great.
[ Applause ]
>> CHRIS LATTNER: Next,
associated values.
Enums are also great because
they're the perfect model
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Enums are also great because
they're the perfect model
for discriminated
union, which is great
when you have two values of
different types that you want
to store in one thing.
Right? Associated values are
very powerful, maybe you went
and tried to write the
obvious thing in either type.
This is a perfect way to
model this, but when you went
to use it, you got something
depressing like this.
Well, this has been very sad,
and none of us like this.
Now with Swift 2 it just works.
[ Applause ]
>> CHRIS LATTNER: Let's
talk about recursion.
Enums in Swift are actually
algebraic data types,
and in other languages,
recursive algebraic data
types are really powerful.
You can do some really
great things.
The problem is in Swift,
the values in an enum
are stored inline.
This means if you
have a recursive enum,
it has an infinite
size, which is hard
for our current devices to hold.
Maybe next year.
There are workarounds for this.
Everybody has seen probably
the box-type that you can turn
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Everybody has seen probably
the box-type that you can turn
into a reference, but that
breaks pattern matching,
it is ugly and horrible.
With Swift 2, there
is a better way.
It didn't quite make
it into beta 1,
but coming soon you'll be able
to mark your cases
indirect allowing you
to express this naturally, and
pattern matching works great.
Let's move on and talk
about scoping [applause].
>> CHRIS LATTNER: So sometimes
you have a name that you want
to reuse or maybe a
resource that you want
to make sure is released early.
We have introduced a new
Do statement allowing you
to introduce an explicit scope.
In this case, we all have to
deal with Internet trolls now
and then, but we try
to keep them bound
as tightly as possible.
Do is really important
when we bring
up error handling
later in the talk.
But taking Do as a keyword led
to some potential ambiguity.
It's not ambiguity
for the compiler;
it's ambiguity as we read code.
You don't always see the bottom
of a big, long statement,
and we have a Do While loop.
To make it superclear by just
looking at the introducer
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
To make it superclear by just
looking at the introducer
for a statement of what it does,
we want to make it superclear,
what something does by looking
at the introducer
keyword for the statement.
So we have taken the Do While
loop and renamed it to Repeat.
You can immediately tell from
the top it that it is a loop,
making it really
easy [applause].
>> CHRIS LATTNER: Let's
talk about option sets.
Option sets are a
lightweight, superefficient way
to represent a set of Booleans.
You may have seen them if you've
worked with various Cocoa APIs,
and you use the see the See
Like syntax to Or them together.
The basic syntax like this
is actually pretty nice.
The problem is, when you get
to the other syntaxes you end
up using, it is a bit less nice.
You create an empty-option
set with nil --
it doesn't make sense
because option sets
and optionals are
completely different concepts
and they're conflated together.
You extract them with bitwise
operations, which is a pain
and super error-prone, and
you can get it wrong easily.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
and super error-prone, and
you can get it wrong easily.
With Swift 2 we have
taken the option sets --
this is even worse, because
Swift 1.2 had a first-class set
type, and now the
combination of all
of this makes option sets seem
like an archaic throwback to C,
which they are [laughter].
>> CHRIS LATTNER: But
Swift 2 solves this.
It makes option sets set-like.
That means option sets
and sets are now formed
with square brackets.
That means you get empty
sets with an empty set
of square brackets, and you get
the full set of standard set API
to work with option sets.
It is supereasy and great.
[ Applause ]
>> CHRIS LATTNER:
Now, it is also nice
because you can define
your own option sets
in an easier way now.
Now all you have to do is
define your own set type,
or struct type, struct type for
your set, and have it conform
to the new option set type
protocol, find storage
to hold your bits, and
define the elements you want
for your option set.
With just a simple definition,
you now get all the
syntax we talked about.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
you now get all the
syntax we talked about.
The thing that's
supercool about this,
it doesn't require
any compiler matching.
This is done automatically
through a new feature called
Default Implementations
and Protocols provided
by option set type.
We don't have time to talk
about Default Implementations
and Protocols in detail here,
but we have a session talking
about protocols going
into it deeply.
It is great.
Let's talk about
functions and methods.
Swift unifies functions
and methods together
into a single func declaration.
This is a great thing that pulls
two disparate concepts together
in the type system into a
beautiful functional core.
This beautiful functional
core is a key part of Swift
that immediately falls
apart when you try
to call these things
because they take different
argument labels.
This has been a huge
pain for a lot of people.
If we look at where
this came from,
Swift was following the
precedent of Objective-C.
C doesn't have argument labels,
argument labels are
superimportant for methods
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
argument labels are
superimportant for methods
in Objective-C, and
Swift followed.
With Swift 2, we
fixed this and more.
Now functions and methods have
the same declaration syntax,
and they work the same way.
Now when you call
a global function,
you provide argument
labels by default.
Everything is uniform.
[ Applause ]
>> CHRIS LATTNER: So the
key thing to know here is
that this affects
pure Swift code.
If you declare a global
function in Swift,
you get this behavior
by default.
Functions imported from
C continue to behave
in the same way they always
have because argument names
in C functions are
not part of API
and are not thought
about as API.
But we like Swift
code going forward
to include argument
labels on functions.
If you look deeper
in what's going
on here, there is even more.
Swift functions take parameters,
parameters can have two
different names for each value.
So when you declare
something with the syntax,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
So when you declare
something with the syntax,
you're actually getting
a default behavior.
The two names that a parameter
can get are an external name
that the caller sees
and the internal name
that the implementation sees.
By default that first
argument has no label shown
to the external client of it and
has a name you're probably using
when you implement the method.
Likewise, the second and
later arguments all default
to being the same
inside and outside.
And that's why you
see this behavior
of having an argument
label for that argument.
The great thing about
this model is
that when you understand
this, you can customize it.
For example, in this case, it
would make sense to have a label
on that first argument
so you know what it is.
You can do that simply by
duplicating that argument name.
It is simple.
Similarly, if you want
to remove something,
you can explicitly set the
name of that to the underscore
to say remove this
argument label.
In doing so, we have
committed one
of the most heinous
naming crimes imaginable
by having a Boolean
without a label.
Go me! The even better
thing, this whole change,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Go me! The even better
thing, this whole change,
this makes labels much more
prominent in the system.
This is great for having
APIs that are friendly to use
and means that we can simplify
away a ton of complexity.
So now functions and
methods work the same,
but we can also get
rid of special rules
for the default arguments and
there is the weird pound syntax,
nobody remembered what it
did, so now that's gone too.
It is much better.
We'll talk about the compiler
and talk about the warnings
and the error messages
compiled by the compiler.
Here is code that's
reasonable code,
maybe you have written
something like this before,
where I'm trying
to update a point.
If you gave this a
Swift 1 compiler,
it would produce
something like this.
I don't know about you, but
that's not helping much.
Swift 1.2 made this better.
Swift 1.2 made the error
message actually tell me
that there is a problem.
Now I see that indeed I
cannot assign to this.
Of course, this is not
good enough either.
We have continued to
invest in the error messages
and warnings produced
by the compiler,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
and warnings produced
by the compiler,
and in Swift 2 it says,
hey, you can't assign to x
because Self is immutable.
And Xcode will tell you
that you can fix this
by marking the method
as mutating.
This is a great way I
think many people --
it will help many people
understand the mutability model
in Swift better and lead
to better code everywhere.
[ Applause ]
>> CHRIS LATTNER: Of course,
this is just one example.
There is a bunch more.
Another example of
warnings we have added are
for if you have a variable that
can be declared as a constant,
we now produce a warning, say,
hey, use Let instead of Are.
The Swift migrator also
automatically moves a lot
of code to using Let
instead of Are in many cases.
We'll warn if you declare
a value, either Let
or Are, but don't use it.
We even have warnings if
you use a functional method
and then ignore the result
because you probably meant
to use an in-place
mutating method instead,
and we can produce
warnings for those.
Those are simple examples.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Those are simple examples.
Let's talk about the SDK.
It's a core part of Swift, it's
how well it works with Cocoa.
With plain Objective-C APIs,
the Swift compiler has no idea
whether pointers can be null
or not and what the element
types of collections are.
We have introduced a
number of new features
to Objective-C including the
ability to express nullability
and the element types of
collections in Objective-C.
There is a whole bunch
of other features
that make a great experience
for Objective-C code in Swift.
The best news about this, is
that the framework engineers
at Apple have done a phenomenal
job adopting all these modern
Objective-C features, and
the Cocoa SDK in general
across all the platforms
feels great in Swift
with no work on your part.
However, if you have Objective-C
code, maybe you're mixing
and matching with Objective-C
code in your project,
or maybe you have an Objective-C
framework that you want
to be beautiful and
awesome in Swift,
go to some of these sessions
later today to learn more
about these features so you
can provide a really great
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
about these features so you
can provide a really great
Swift experience.
You probably have to watch
one of those on video.
Let's talk about unit testing.
Across the entire tools team,
testing is superimportant.
Testing is great in Swift until
you bring up access control.
The problem is that
Swift requires you
to mark symbols Public to be
visible to your unit test bundle
so you can test them, leading
to tons of stuff being public
that really shouldn't be.
Swift 2 and Xcode 7 has
solved this problem.
Now your code is automatically
built in a special mode,
meaning that for your tests they
can get access to your public
and internal symbols by default.
You have to use the
new app to --
[ Applause ]
>> CHRIS LATTNER: The
even better part of this,
not only is it easy,
it is also --
you still get the right behavior
for your release builds,
so you get the performance
and the protection
benefits of access control.
We have a bunch of
talks on testing,
UI Testing in Xcode will
be a fantastic talk.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
UI Testing in Xcode will
be a fantastic talk.
I highly recommend it.
Let's talk about rich comments.
Playgrounds, they're
great, and Xcode allows you
to build beautiful
Swift playgrounds right
in the editor using a comment
syntax, a rich comment syntax.
The syntax is a variant of
Markdown, which is a great,
well-known, very
popular, loved syntax.
We brought that to
documentation comments as well.
This means you can build rich
and pretty beautiful
documentation comments,
and it shows up for
clients in your API.
So if you're producing
a library,
you can do great things here.
Finally, the migrator in Xcode.
As soon as you open the
Swift 1 project in Xcode 7,
it will prompt you and say, hey,
I can upgrade this
to Swift 2 for you.
It takes you through
a couple steps,
you can pick your targets,
and then it gives you a dif.
The Swift 2 migrator is
actually pretty phenomenal.
It covers the vast majority
of the problems and cases
that you will see moving
from Swift 1 to Swift 2,
including the error handling
model, moving things to methods,
changes to the SDK, a ton
of the option set changes,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
changes to the SDK, a ton
of the option set changes,
all of these things are
built in the migrator,
and it does a great job.
There is a ton of new stuff
in Swift 2, we don't have time
to talk about all
of it right now.
If you're interested
in more detail,
I recommend taking a look at the
Swift programming language book,
there is a new version up.
Also the Xcode 7 release
notes talks about a lot
of these changes in more detail.
Let's move on now and talk
about pattern matching.
So probably the first place you
encounter pattern matches was
with the if-let statement.
It is a great way
to take an optional,
conditionally unwrap it,
and then bind that result
to a name with safety.
It is a great thing.
There can be too much of
a great thing, of course.
One of the things we saw is that
there is the "pyramid of doom,"
which is what happens when you
get too many if-lets all nestled
together, and suddenly your
code is fifteen levels deep
and you can't understand it.
Swift 1.2 solved this problem
by introducing a compound
condition into if statements.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
by introducing a compound
condition into if statements.
Which makes this really natural.
You can check multiple optionals
and Boolean conditions right
inline, and it is a lot nicer.
This didn't solve the
problem of early exits.
I'll show you some of what
might be the most horrible JSON
processing code you
can ever imagine.
It will get better over time.
Let's go with this.
Here I'm pulling various fields
out of an untyped
JSON dictionary.
So I'm pulling out a name,
converting it to string,
produces an optional, if it
doesn't match, I bail out.
Likewise, pull the year
out, convert it to an In,
if that doesn't match, bail out.
This pattern is very common
if you're pulling lots
of values out, it is better
to do this bailout approach
than to deeply nest your code.
The problem with this
approach is that then you have
to force unwrap the optional
values when you're done.
Here I only use them once,
but if you have a bunch
of code using them, you're force
unwrapping this everywhere.
You can do things
to factor this,
so the implicitly unwrapped
optional is a great way
to factor force unwraps into
a Mecca of unsafety for you.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
to factor force unwraps into
a Mecca of unsafety for you.
This is maybe not the
right approach either.
This is ugly.
We introduced a new
Guard statement.
The way to look at Guard is
it does a check and then bales
out if that check doesn't match.
You can do a lot of things
in a Guard statement.
Here we're doing
our optional check
and we're binding
a value to a name.
The way that it works,
the way it can work,
is that it guarantees that your
Else exits the current scope.
You can do this in one of two
ways, either return, throw,
brake, a lot of ways
to exit a scope.
That's fine.
You can also call a No Return
function like Precondition Fail
or Abort, and that's a
good way of stopping.
What this guarantees for the
compiler, it knows with safety
and certainty that the names
that are bound can be visible
after the code in
the fall-through.
If we take this to our
example, our example gets nicer
because now we can use
Guard, and we have very safe,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
because now we can use
Guard, and we have very safe,
concise checks for this
as we would expect.
The other nice thing about
this, this builds on the rich,
compound conditionals
we had with If.
Now you can merge them together
and check multiple Boolean
and optional and other cases
we'll talk about later right
in line in your Guard statement.
It is pretty nice.
[ Applause ]
>> CHRIS LATTNER: Let's
talk about the more exciting
and powerful form of
pattern matching, switches.
Switches are I think maybe
other people's favorite feature
of Swift because you can do
so much with pattern matching
in a switch statement.
You check against an
optional like here,
you can do class hierarchy
checks, you can check
against ranges, there is no end
to what you can do in a switch.
They're great when you want
to write a lot of cases,
but they're kind of
syntactically heavyweight
when you want to
check just one case.
They have to be exhaustive,
you have to have a
default, it is a pain.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
you have to have a
default, it is a pain.
What we have done, we have taken
the power of pattern matching
with switch and with
case and brought it
to the other control
statements in the language.
This example can be written
with a new If case, check,
and you can pattern match
and bind variable
names right in line.
[ Applause ]
>> CHRIS LATTNER: We
have gone further.
Another great statement in
Swift is the for...in loop.
So it's very common to
want to do some amount
of filtering in a for...in loop.
Some languages have gone so far
as to introduce entirely
new language constructs
like list comprehensions to
model this kind of pattern.
With Swift we have
done two things.
We have added the ability
to do a simple Boolean
a filter right inline
in your for...in statement.
But you can also do full-on
pattern matching right in your
for loop to give you
powerful conditions.
[ Applause ]
>> CHRIS LATTNER:
That's all I have to say
about our quick tour
of pattern matching.
We talked about the new Guard
statement, which is great
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
We talked about the new Guard
statement, which is great
for early exits, talked
about bringing pattern matching
pervasively to the language,
and we didn't talk about some
of the other improvements
that you can discover as
you start to use Swift.
Thank you.
I will hand it over to
John, who will tell you
about availability checking.
[ Applause ]
>> JOHN MCCALL: Thank
you, Chris.
We often roll out new
features, you may have heard
of one called Force Touch.
Force Touch is mostly a hardware
feature, of course, but it comes
with a number of APIs.
Like this one on NSButton,
letting me change how a button
responds to drags over it.
If I want to adopt
this in my own app,
that's pretty easy, right?
I have to write some
new event handling code
and then I just need
to take my button
and set this spring
loaded property on it.
The problem is that this may
work great on my dev machine,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
The problem is that this may
work great on my dev machine,
but when I farm it out
to my test hardware,
I'm going to get a crash
like this almost certainly.
That's because this
is a new API.
It was introduced in X v10.3.
And, like many of you in this
situation, you still have a need
to support an older
version of the OS.
How would I fix this?
The way I used to
fix this is, okay,
I'm getting this error message
about the method not existing,
let me check to see
if the method exists.
One way -- there are a
lot of different idioms
that people have
developed for doing this,
this is common idiom,
using Responds to Selector.
The problem is that this is a
fraught, error-prone pattern.
For example, I actually have to
figure out what the selector is,
the mapping from some
Swift language feature back
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
the mapping from some
Swift language feature back
to some Objective-C selector, it
is really not the sort of detail
that anybody should
need to know.
It is also, you know, not being
checked for me by the compiler
because I'm sort of
intrinsically escaping the sort
of checking that the
compiler provides.
For example, in this case
I have actually forgotten
to add this colon, which means
that check will never
actually be true.
In Swift 2 we have
a better solution.
By default, the compiler
is checking to make sure
that you don't use any
APIs that are not available
on your minimum deployment
target.
[ Applause ]
>> JOHN MCCALL: If I
do something like this,
I'm always going to
get a diagnostic.
What that lets me have is a
sort of safe core assumption
that as long as my code --
as long as my project fully
compiles, it is at least free
of this sort of trivial
deployment [unintelligible].
Now that's not the entirety
of the compatibility story,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Now that's not the entirety
of the compatibility story,
of course, but this is a great
way to help you adopt new APIs.
I do want to use this.
How do I do that?
We added a new #available
condition.
In #available, you list out
the OS versions you want
to make sure you test for.
And at the end you use
this star to make sure
that if there are any new OSs
that you haven't
written this code for,
you at least get diagnostics
about the availability there.
Here I've used an If statement,
but I could have used
the Guard statement
that Chris showed us before.
It is the exact same
sort of condition logic
in all of these places.
That's it.
That's availability checking.
We think this is a great new
way for you to be able to adopt
and take advantage
of the new features
of new OSs automatically
and safely in your projects.
We'll talk more about this
in a talk later today.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
We'll talk more about this
in a talk later today.
I really suggest
that you come to it.
The next thing I want to talk
about is protocol extensions.
Extensions are a really
great feature in Swift.
I can take an arbitrary
type like Array
and add my own methods to it.
It is not necessarily obvious
why this is an important thing,
but a method is a core part,
it is how the native APIs
of that type are expressed.
When I'm adding an extension,
I'm really adding new
functionality to a type
that feels just as first
class, just as core to the API
of the type of anything
of the designer
of the type may have
already added.
That has a lot of advantages.
Here I've added a
Count If method,
that simply calls a closure for
every other array and counts
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
that simply calls a closure for
every other array and counts
up the number of times
that return True.
There is nothing in this
method that's actually specific
to array at all.
This ought to work for
an arbitrary collection.
Unfortunately, in Swift
1 I couldn't express this
as a method anymore.
To make this generic over
an arbitrary collection,
I would have had to write
something like this.
As a lot of you have
pointed out to us,
this is not exactly optimal.
The first thing is that this
is a lot of extra syntax,
there is sort of a blindness
about all of the angle brackets
in it, all of the extra crust
to just make this generic.
The second thing is it
is no longer a method.
Because it is no longer
a method, first off,
it doesn't feel like a
natural part of using the type.
Second off, it is a
lot less discoverable.
It won't show up in any lists
of the functionality on array,
and in particular it's not
going to show up in the list
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
and in particular it's not
going to show up in the list
of functionality provided
by code completion.
Which means that great, you
have written this awesome Count
If thing, but nobody using this
will realize it exists unless
you point it out to them.
All right.
Let's go back.
We had this extension on array
just to add the method to array.
Why can't we just
extend -- I don't know --
everything that implements
collection type?
In Swift 2, I'm happy
to say that now you can.
You extend collection type
rather than extending array.
When you do this,
you're automatically
adding the method everywhere
to every single type that
implements collection type,
not just from the standard
library or anything but even
in your own types that
happen to conform to this.
Not only is that great for
writing your own generic code,
but we found it really lets
us overhaul a lot of things
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
but we found it really lets
us overhaul a lot of things
that we weren't really
happy with about
with the standard
library in Swift 1.
There were a lot of
things in Swift 1 that had
to be global functions
because they had to be generic
or because we wrote
them generic.
Then worse, in order to
make the methods we sort
of special-cased certain
types, like array has a lot
of these map and
filter methods on it.
Other types like
Set may not have.
In Swift 2, this functionality
is going to be available,
this sort of filter and map
functionality is expressed
with extensions, meaning it is
available on every single thing.
It makes it a lot
more discoverable,
means using the standard
library is more uniform,
and we really think
that you'll love it.
[ Applause ]
>> JOHN MCCALL: I really
haven't even gone into a third
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
>> JOHN MCCALL: I really
haven't even gone into a third
of the complexity in the awesome
new power of this feature.
We're going to have a
talk dedicated to this,
it is a great talk tomorrow.
I strongly suggest
you come to it.
It is about the great
new design patterns
that protocols enable in Swift.
The rest of the talk is going
to be about error handling.
I don't think anyone
really likes thinking
about error handling.
It is always sort of this guilty
thing in the back of our minds
if you're at all like me.
It is really, really important.
When we were looking at
what we could do to Swift
that would really
make it a more robust,
more expressive language,
we said, okay,
the most important
thing here is going
to be doing something
about error handling.
When we looked at the
solutions that were out there,
in other languages, in Cocoa,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
in other languages, in Cocoa,
we weren't really
happy with any of them.
They all have sort
of major pitfalls
that we didn't really like.
Some of them, you know,
are based around propagating
the errors around automatically
like with NSError
in Objective-C,
what that creates is a lot of
repetitive error-prone code
where you end up having
to duplicate logic
all over the place.
That means it is very
easy to get wrong.
More importantly, when
you propagate error values
around yourself, the
implicit default behavior is
that you're ignoring errors.
That's just never
the right default.
You should have to think about
errors at least a little bit.
On the other end of the
spectrum, there are languages
that propagate errors
around implicitly,
like with exception handling.
But we didn't like how any
of those worked either.
There is too much that was
implicit, it was too easy not
to think of errors again,
and you end up with pitfalls
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
to think of errors again,
and you end up with pitfalls
where you just didn't
understand what could go wrong
in your program.
You didn't understand
how control could flow
from one place to another.
Again, it wasn't a safe,
reliable programming model.
There are really three different
ways that functions can fail.
One way is that they can simply
-- a lot of functions just fail
in one fairly simple,
innate, obvious way.
For example, unless
you're running a compiler,
you probably don't care
why parsing an integer
out of a string failed, this
isn't going to be something
that you're getting
the juicy details of
and reporting to the user.
Probably you want to
handle that directly.
That's already something
that when we looked at it,
this is handled extremely
well in Swift already,
just with optional results.
We didn't think we needed
to do anything here.
We're really happy with
how that works already.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
We're really happy with
how that works already.
On the other end of the
spectrum, there are a lot
of things that are logic
failures in your program
that are programmer
mistakes, assertions,
indexes out of bounds,
the vast majority of ways
in which people use
NSException, that kind of thing.
For these things, they
really actually shouldn't
be recoverable.
When you can recover
from this kind of thing,
you're just promoting a
less stable program overall.
You don't know what state
your program is actually
in if you randomly recover
from an index out of bounds.
You could even be
creating security problems
in your application.
In the middle, there is
this large spectrum of APIs
that can fail in a
very rich set of ways.
That's really what we
wanted to focus on.
The things that you today in
Cocoa would use NSError for.
I want to work through
an example for you.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
I want to work through
an example for you.
This is a preflight
method, I have some sort
of operation I want to
prefly to make sure it works.
This is something that a lot of
you have written I think before.
I'll check to see whether some
file is actually reachable,
and then I'm going to
reset some state associated
with the operation.
Now checking whether the
resource is reachable,
this is an operation
that can fail.
It can fail in a
wide variety of ways.
It should report
something back because, hey,
somebody calling
this really may want
to know why something
isn't reachable
and maybe treat it
differently depending on why.
If I wanted to use NSError
for this, this is kind
of what the code
ends up looking like.
I'm taking this error, I'm
propagating it out to my caller.
There are things
to like about this.
There are a lot of things
we don't really like.
It adds a lot of
boilerplate to my logic.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
It adds a lot of
boilerplate to my logic.
I had a tight little two-line
function, now it is turned
into this -- you know,
it has this If statement,
extra nesting, the
extra parameter,
there is a lot here
whose sole purpose is
to express there is an error,
and we're propagating
it out to the caller.
Worse, again, there
is a convention here,
that's a convention
you need to know about.
And it's a convention you
have to implement manually,
and the compiler's not really
going to help you with it.
Again, I made a mistake here.
The convention is that an error
happens when you return False,
I'm actually checking
it the wrong way.
I don't know why they
trust me to even work
with compilers [laughter].
I need to add this
Not here in order
to get the behavior I want.
Okay. That's -- well, no, sorry.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Okay. That's -- well, no, sorry.
Those are the downsides of this.
There is actually a lot
that we like about it too.
The first thing is, it is
obvious from reading this code
that check resource is reachable
is something that can fail.
It says right there in the
name, it talks about errors,
it has the explicit
error handling thing,
this explicit error parameter.
Similarly, it is obvious
that preflight is an
operation that can fail.
Again, explicit error parameter,
the return value, et cetera.
The third thing is, there
isn't implicit control flow.
I can just look at this
thing and understand
where all the jumps in it are.
I can analyze my code statically
as a human, not as a compiler.
As a human, I can look
at this code and reason
about what it is
doing without needing
to know every last detail
of every single function
that I'm calling.
All right.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
All right.
Now let's go back
to the example.
This is how it looked before.
What's going to happen if I
try to compile this in Swift?
I'm going to get
an error message
because I'm not handling
the error.
There are two components to
handling errors in Swift.
The first is that whenever
you're calling an API
that can fail, you have
to use this Try keyword.
The Try communicates, it
is really there primarily
for someone reading the code.
It communicates to you, hey,
this is something that can fail.
That means when you're
coming back,
when you're maintaining
this in the future,
I know straight off --
Reset State, that's
not necessarily going
to be called every single
time to this function.
That may be a really
important thing for me to know.
When I'm writing this code in
the first place, it is something
for me to think about.
Hey, should Reset State be
called every single time I exit
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Hey, should Reset State be
called every single time I exit
the function?
For a preflight operation?
Maybe. That alone isn't enough,
I'm not actually handling
the error in any way.
This is because in Swift, by
default, functions can't throw.
That's actually a really
core aspect of our design
because what it means
is errors are bounded.
You don't have to think about
literally everything being able
to throw an exception
like it can in Java or C#
or basically every
language using exceptions.
Instead, it is really
just very specific things
that you know you need to worry
about whether they can throw.
And when you call
them in your code,
it is always marked with Try.
That combination
communicates a lot.
Okay. Well, suppose I do want
to just propagate the
error out to my caller.
In order to do that, all I have
to do is tell the compiler, hey,
it is okay for this thing
to throw the error out.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
it is okay for this thing
to throw the error out.
I do that with throws.
That may not be how I
want to handle this.
This is a preflight operation.
I probably want to
just swallow the error
and tell whoever is
calling me whether
or not the preflight succeeded.
To do that, I have to handle it.
I handle it by writing
this Do Catch.
Any code that's within the Do,
any error arising within it,
is forwarded, sort of filtered
through all of the catches.
So what comes after
a catch, well,
anything that you could
write in a switch.
The entire power of Swift's
pattern matching syntax is
available in a catch.
As a very simple, common
syntactic refinement,
catches alone like this is
a shorthand for catching it
and binding this special
error variable to it.
I could also write a much
more elaborate thing.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
I could also write a much
more elaborate thing.
For example, I may want to
treat certain kinds of error
as special, maybe they're
acceptable in my preflight.
I don't know why the file
not existing would be okay,
maybe I really want to check
if it exists and, you know,
isn't actually usable for
some permissions reason.
If I wanted to, I can pattern
match against the error code
and domain directly like this.
As an aside, there is a third
way of "handling errors."
It does often happen that
you set up preconditions
such that you know that a
particular call to something
that can formally throw
can't actually throw.
For example, maybe this file
is actually in my app bundle
and I know that if I can't read
a file in my own app bundle,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
and I know that if I can't read
a file in my own app bundle,
something is really,
really wrong.
There's probably no real
way to recover from this.
So with this common pattern,
where you really
want a fatal error
because an error is thrown,
it has a very compact syntax
associated with it, this Try!
All that really does is creates
an assertion that the code
within the Try doesn't
actually throw.
If it does, your
program will crash just
like any sort of
assertion failure.
That's something that you
can debug very, very easily.
It is not something you
want to use all the time,
but when you need it, it
is really, really handy.
Going back a bit.
I caught an error.
What kind of thing is an error?
Well, we have a protocol built
in to the standard
library called ErrorType.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
in to the standard
library called ErrorType.
You can throw any value whose
type conforms to the ErrorType.
When you catch something,
that thing that you're
pattern matching
against is an arbitrary
value of ErrorType.
We think it is actually
really important
that we don't track errors more
precisely than just whether
or not an error was thrown.
It is not like Java, where you
end up with a pedantic list
of every single exception that
might have been thrown out
and then you end up with this
really complicated propagation
problem every time
you change errors.
Just tracking whether an error
can be thrown is usually good
enough, almost always.
We think this is a great model.
You can make your own
types conform to ErrorType.
This is a process
that's a lot easier
to do than it was in Cocoa.
Enums are a great way
of expressing this.
They're a great way to express
a group of related problems,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
They're a great way to express
a group of related problems,
just like they're -- you know,
so that's especially true
because you can associate data
with each case in an enum.
If I want to report a
richer error message
with maybe something
about the --
maybe I'm checking for some
invalid state and I want
to remember what the
invalid state was,
I can embed that directly in
my enum as an associated value
for that particular case.
All you need to do in order
to make an enum usable
as an error is make it
conform to ErrorType.
The compiler automatically
handles the details
of the synthesis.
This is much better
than the process
of creating a new NSError domain
and associating things with it.
We think that this will
really help you make great,
expressive error-throwing APIs
in your own code
when you need to.
Let's go back to that
example that Chris had
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Let's go back to that
example that Chris had
up before, this JSON processor.
Here I'm returning an error
back using an Either type
in the string.
Let's make this look more
like it would in Swift.
The first thing is,
instead of the string,
I'll use that data error
enum I just talked about.
I just have to throw, use the
new throw statement in order
to throw those values,
and that just works.
The other side of this,
of course, is I'll need
to change the return type.
I'm no longer returning
an Either type.
This isn't something that
every single caller has
to carefully micromanage
the return value
of in order to check for errors.
I just change it so
that it returns Person
and is also a throwing method,
then I don't have to mess
around with these
little details.
Let's make a new
example that culls
that method we just created.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
that method we just created.
We parsed out, we have a snippet
of JSON, we parsed out a person.
Now we'll use that to parse out
an entire sales record involving
that person and some item.
Sometimes it happens that you
need to, you know, this is a bit
of a contrived example.
Sorry. Sometimes I want to
observe this kind of process.
I'm going to have some sort
of delegate, I'll let it know
that I have started
reading a sales record.
I have now told it
I started reading,
obviously I should tell it
when I'm finished reading.
I can add that code down here.
The problem is I'm not
doing anything right
with error handling.
It is really easy for
all of these -- like --
if my delegate actually has
Careful and Variants set
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
if my delegate actually has
Careful and Variants set
up around necessarily getting
called every single time --
getting called when
the sale finishes.
If my delegate has variance
it wants to maintain
about getting called on
both ends, I'll mess them
up if I actually
fail the process.
This is a sort of problem
that comes up a lot
and makes error handling
seem so fragile.
Okay. One way I could
solve this, of course,
is that I simply add my call
to Did End Reading Sale on both
of these throw sites
and then, of course,
I'm still not handling this
call down to Process Person.
In order to do something there,
I have to add this Do Catch.
This is a really, really --
one, this is incredibly verbose.
But it's also really error
prone because it is easy for me
to add new code to this,
new kinds of processing
that it then immediately
will get out of date
if I actually do something.
If I forget to add Did
End Reading Sale along
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
If I forget to add Did
End Reading Sale along
that particular path.
Swift 2 has a much
better option.
It is called Defer.
A Defer statement
creates an action.
When you execute it,
that action is going
to be executed no matter how
the current scope is left.
If I return out of it, if I fall
out of it, if I throw an error
out of it, no matter how,
I know that that thing
is going to be executed.
That means that as
someone reading this code,
maintaining this code,
I feel perfectly confident
Did End Reading Sale will be
executed no matter what I do
to finish reading the sale.
That's a really, really
valuable thing to know.
[ Applause ]
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
[ Applause ]
>> JOHN MCCALL: I want to make a
quick note about implementation.
Some of you who are used to
exception handling may be aware
that exception handling in
many languages is implemented
in a way that's very,
very highly biased
against errors actually
being thrown.
It is often three, maybe even
four orders of magnitude slower
to return out of a function by
throwing an error than it is
to simply return out
in the normal manner.
Now, that's necessitated by some
aspects of the language designs.
It is not really something that
we wanted to imitate in Swift.
All you really need
to know here is
that the Swift implementation
here is far more balanced,
much more like, basically, an
If statement in the caller.
That means it is not completely
free in order to call something
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
That means it is not completely
free in order to call something
that can throw an error.
But it means that you
don't have to worry
about our error-handling
feature being so expensive
that you can't use it in
order for the actual reasons
that you need to if
you do need to care
about the efficiency
of the error path.
Finally, I just want
to note, Swift,
the Swift error handling
design works beautifully
with Cocoa APIs.
We automatically recognize the
most common conventions you see
in Cocoa.
For example, methods that
have an NSError Out parameter
and return Bool automatically
become throwing methods
and the Bool return
value goes away.
Similarly if it returns an
optional result, we recognize
that pattern as the nil
indicates an invalid thing,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
that pattern as the nil
indicates an invalid thing,
and it no longer returns an
optional result because the nil,
of course, is subsumed
within error handling.
[ Applause ]
>> JOHN MCCALL: Just with these
two very simple rules we found
that the vast, vast majority
of APIs in the system import
and automatically work with this
new Swift error handling model
seamlessly and beautifully, and
we think this is a great new way
to handle errors in Swift.
I really strongly suggest
that you check this out.
You probably don't really
have much of a choice,
they're all over the
place [laughter].
>> JOHN MCCALL: You know, we
really are proud of this design.
We think it is going to
greatly improve the robustness
and the expressiveness
of writing code
and let you design your own
APIs that work just wonderfully.
Let me sum up.
We have been working in Swift
2 really hard to present,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
We have been working in Swift
2 really hard to present,
to give you a new
language, really flesh
out the core aspects of
programming in Swift,
using the tools in
Swift, giving you a safer,
more robust environment, and
generally making things great.
An invaluable tool to
us, this entire time,
has been your feedback.
We really, really appreciate it.
We are listening, I promise.
If you have things to say to us,
of course you can
simply use bug reporter,
but you can also email Stefan
Lesser, come on the dev forums,
most of us are there
all the time.
We're really happy to
respond to any question,
hear your feedback about it.
We really, really value you.
Thank you very much [applause].
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Have a great WWDC 2015.
[ Applause ]