Transcript
[ Music ]
[ Applause ]
>> Hello, and welcome to
Getting Started with Swift.
I'm Dave Addey, and together
with my colleagues Brian
and Alex, I'd like to
give you a quick overview
of the Swift Programming
Language.
In the next 60 minutes you'll
learn enough about Swift 3
to be able to read
Swift code and hopefully
to start writing it, too.
So let's start by taking a
look at some of the basics
of the language, and here's
some code I wrote two years ago.
I've written a constant,
indicated with let,
it's called language,
and it's a string.
And this colon in the middle,
you'll see this often in Swift,
this means is of type.
So language is of type string,
and I've just given it a value
So language is of type string,
and I've just given it a value
of Swift, using a string
literal in my code.
Let's make a few more.
Let's have an integer
called introduced.
We'll that to 2014.
And a Boolean called isAwesome.
And we'll set that to true.
Now, you might notice that none
of these values need to change.
The language's name
isn't going to change,
nor is the year it
was introduced.
And two years on,
Swift is still awesome,
so that can probably
be a constant as well.
And this is the general
principle in Swift.
If something doesn't
need to change,
we declare it as a constant.
Now, you might also
notice a naming trend here.
Constants and variables tend to
be written as lower camel case,
and types, such as String, Ints
and Bool, is upper camel case.
Now, if we look at the
things on the right here,
it's pretty obvious, actually,
that what I want is a string,
an integer and a Boolean,
and where this is the case
Swift can infer the type for us.
We don't actually need
to write it ourselves.
We don't actually need
to write it ourselves.
So you still get things
that are explicitly typed,
but you don't have to
write as much code.
So that's some constants.
What about variables?
Things do sometimes
need to change.
So here's a variable
indicated with var,
and it's for the
version of the language.
This has changed.
So let's bring it
up to date to 2016.
Now, if I try to do
this for a constant,
if I try to set isAwesome to
false, Swift would tell me
that it was an error,
and quite right, too.
So a common thing to want
to do in programming is
to build strings
from other values.
We could do this by
concatenating strings together,
as shown here, but Swift
has a neater way to do this,
known as string interpolation,
and this is how it looks.
And we can put strings and other
values inside a larger string
by wrapping them in parentheses
preceded by a backslash.
Here we're making the
message, Hello WWDC.
We're not just limited
to strings, however.
We can add other values as well,
such as integers here
putting the year in.
And we could even add
expressions as well.
And we could even add
expressions as well.
We can add year +
1 for some code
that would work for
a future year.
Now, strings in Swift are
fully Unicode friendly.
They're just as happy
with ASCII as they are
with dog, cows or with flags.
And the same also
goes for any constants
and variables you create.
You can use pretty much
any character in the name
of your constants and variables.
Talking of [inaudible]
characters,
Swift does all the hard work
of working out what it means
to be an individual character,
regardless of how your string
is encoded behind the scenes
or how it appears on screen.
So here I have a
string called dogString.
You might think that this
has six characters in it.
D, O, G, ?, !, dog face.
But you would be wrong.
There are five characters
here, not six.
This is a special character
called question exclamation
mark, which is really,
really good for expressing
incredulity about dogs.
really good for expressing
incredulity about dogs.
Now, if you don't believe me,
let's get Swift to prove it.
Every string has a
property called characters.
It gives us a collection of
the characters in the string
and we can access the count
property of that collection
to see how many there are.
If we pass that to
the print function,
we find out that we do,
indeed, have five characters.
But if you still don't
believe me, well, let's iterate
over each of those characters
in turn, using the for-in loop
and print each one of
them on its own line,
and you can see we do,
indeed, have five characters;
D, O, G, ?!
and dog face.
Now, in addition to all of these
fundamental types we also have
some built-in collections.
We have array and dictionary.
You might know dictionary
as a hash
or map table from
other languages.
And the easiest way
to create an array
or a dictionary is
with a literal.
This is an array literal
written with square bracketry
on the outside and
commas between each value.
Here we have an array of
four names, four strings.
Dictionaries look very similar.
Their keys and values are
separated with a colon.
Here we have a dictionary whose
keys are strings, the names,
and whose values are integers.
Now, you might notice from
the things in this array
that they're all the same
type; they're all strings.
It wouldn't make sense to insert
something else in a names array,
to have an integer or
a Boolean or a bicycle.
That would be just weird.
For names we always
want them to be strings,
and in Swift we can
express this.
We can say we want an array
of strings, so we know as soon
as we get anything out of it,
we can do string
like things with it.
So this is how we write that.
This is how we write
an array of strings,
a string with square
brackets around it.
But as we saw earlier
on, when it's clear
from the right hand side
what kind of type we want,
Swift works it out for us,
and that happens here as well.
We actually don't need
to write the type.
It's clear we want
an array of strings.
The same goes for
our dictionary.
Here it's clear we want string
keys and integer values,
Here it's clear we want string
keys and integer values,
so Swift can infer
that for us as well,
but everything is
still clearly typed.
Swift has all of the loops you
might know from other languages.
We have a while loop that checks
its condition before first
executing its body.
And a repeat-while loop,
which executes its body once
before checking a condition
to see if it should continue.
And as we saw earlier, we have
the for in loop, which we used
to iterate over the
characters of a string.
But that's not the only thing
the for in can be used with.
We can use it to
iterate over a range.
Here we have a range that
runs through from 1 through 5
and includes most of those
numbers, which we're using
to print five entries
from the four times table.
We write this as 1...5.
That is called a closed range
because it includes
both of those numbers.
However, sometimes it's useful
to have a range that runs
up to one less than
its final number.
Here's an example of
where that's useful.
I have an array of
integers, but I only want
to print the first five.
Now, because array
indices start from 0,
I actually want indices
0 through 4.
So for that we can use the
half-closed range operator,
..< because it runs the one less
than the final number,
in this case 5.
We can use a for-in
loop with an array.
Here we're printing
a message for each
of the names in our array.
And we can also use
it with a dictionary.
Now, note here that we're
iterating over the key
and the value, the name and
the age, at the same time.
And this is an example of
Swift speech known as a tuple,
which lets you combine those
multiple values together
and use them at the same time.
And we'll see another
example of this later on.
And it makes for
much clearer code
when iterating over
a dictionary.
So how would we modify
these collections?
Well, here's my packing
list for WWDC this year.
I've declared it as a
variable so I can change it,
but I've included the
essentials, socks and shoes.
Now, at WWDC 2014 I
forgot to pack my socks
Now, at WWDC 2014 I
forgot to pack my socks
and it was a nightmare, so I'm
not making that mistake again.
So let's check that
the first item
in this array is
definitely socks.
After 2014, if I put it on
the list, it will be first.
We do this using a subscript,
writing an index inside square
brackets after the array name,
and if we print this value,
I have indeed remembered
to add socks.
Socks and shoes will
not be enough, however,
for a week of the conference.
I'll need more things.
So let's append a new item.
Let's append some trousers
to this array as well,
which we do using
the append method.
But there's a problem here.
The conference is in America and
they don't call them trousers,
they call them pants,
and that's going
to cause all kinds of confusion.
So let's change a
value in our array.
Let's change this
to be jeans instead.
Again, we use a subscript
to do so,
to change the item in index two.
Jeans are called the
same thing everywhere,
so this shouldn't
cause confusion.
Now, the conference
is in California
where it is always
hot and sunny,
where it is always
hot and sunny,
so let's add a few more items.
Let's add some shorts, some
sandals and some sunblock.
We can do this using the append
contents of method and pass
in a compatible array,
another array of strings here,
and they all get
appended at the same time.
That said, whilst the
conference is in California,
it's in San Francisco, where
it is not always hot and sunny.
So maybe let's change those
three items and replace them
with a hoodie and
a scarf instead.
We can do this by passing
a range into the subscript.
And note that we're changing
three items with two,
this is still perfectly
valid in Swift.
So what about a dictionary?
Let's modify a dictionary.
Well, here I have my ages
dictionary from before.
I'd like to add somebody else
to it, and I do this just
by adding a value for a key
that's not already there using
a subscript.
Here I've added Justyn.
But thinking about it, it was
Justyn's birthday last week
so this value is now incorrect.
I need to update it, which I
do in exactly the same way.
I just assign a different
value for the same key
and now my dictionary's correct.
What if I want to retrieve
a value from the dictionary?
What if I want to see if we have
an age for Devon or for Daryl,
or perhaps for Daniel?
Well, there might be a value in
the dictionary for these people,
but there might not, and we
need a way to model that,
and this is a great use case
for a Swift feature
known as Optionals.
If we tried this for Amy,
we might expect a
value of 40 perhaps.
But if we tried it for Daryl,
what should we get then?
There's no value here for Daryl.
Think of it like this.
There either is a value in the
dictionary and it's an Int,
that's for Amy, or there's
no value, there's no Int,
as in the case of Daryl.
So what we have here
is an optional Int,
which we write as
Int question mark.
And the question mark means
there might be a value here,
but there might not.
Now, we can check to
see if we found a value
Now, we can check to
see if we found a value
by comparing it against nil.
Nil is just a special way,
a shorthand way of
writing no value.
If we try this for
Daryl, there is no value,
we'd print the message.
Age not found.
If we try this for Amy,
well, we do find a value
so it doesn't equal nil and
so we don't print the message.
Typically, however, we don't
just want to check for a value.
We actually want to
use it if it exists,
and Swift has a really good
shorthand for writing this,
which we write as if let.
Now, this says here the
dictionary contains a value
for Amy, let a new constant
called age equal that value.
And then we can use that
value inside the if statement.
And note that we use it
as an actual integer.
It's not optional anymore.
The if statements checks
for the value, unwraps it
and gives us back
a normal integer
that we can do integer
like things with.
So we've seen a few
if statements so far.
Here's another that
prints a birthday message.
There are two things to
note about this statement.
Firstly, we don't
need parentheses
around the conditions so we can
just write them as they are.
And secondly, we do add
curly braces around each part
of the statement to make
it explicit which bits
of code are going to be run.
In addition to if, we also
have a switch statement,
and this is useful for
writing maybe more complex,
more powerful matches.
This switches over the
current value of a constant
or a variable and matches
certain cases that can occur.
So, for example, we can have
the case here for an age
where the value is
one and we want
to print a simple happy
first birthday message.
We can also match
against ranges.
Here matching any value that
would make someone a teenager.
And we can match more complex
patterns, such as this one,
which says that a temporary
constant called decade equal the
value that we're
currently matching,
check if it will divide
cleanly by 10, and if so,
use it to print a
special birthday message
use it to print a
special birthday message
for anyone who's
just turned 30 or 40
or some other significant age.
But there's a problem with
this statement as it stands.
What would happen if we wanted
to print a message for someone
who was 41, or 97 or 56?
Well, they wouldn't
get a message,
and that seems really
unfortunate,
especially on their birthday.
Frankly, Swift helps
us out here.
Swift makes sure that every
switch statement is complete,
that you don't accidentally
forget one
of the cases that
you might need.
And in this case we can
add a default statement
which catches every other case
we hadn't already caught above
and just says, Happy
plain old boring birthday.
Here's another switch statement.
This one takes a string value,
user name, and a Boolean value
that indicates whether that
user's password is valid,
and we'll use both of these
values together to work
out an appropriate message to
display when this user tries
out an appropriate message to
display when this user tries
to log into a restricted area.
And to do this we can
switch over both values
at the same time using a tuple,
same as we did earlier on.
So this means we can
write some really,
really interesting use cases,
some really interesting
switch cases.
We can have the case where
the user name is admin
and the password is
true, and print a message
to welcome back our
administrator.
Now, in the case of a guest we
never want to allow the guest
into the restricted area even
if their password is valid,
and so we can ignore
the password
by writing an underscore,
and this means just match
any possible value here.
For all other users we actually
don't care what the user
name is.
We just care about the password.
So we can ignore the user
name again, and instead,
we've switched on
what we want to do
with the password's validity.
To do this we create a temporary
constant called IsValid,
and we then use the ternary
conditional operator,
that's the question
mark and colon here,
to say if it's valid,
use this message;
to say if it's valid,
use this message;
otherwise, use this message.
So let's run that through
for a few examples.
If we take our administrator,
the password's valid,
they get a special administrator
message, as expected.
If we try this for
a guest, well,
even though their password is
valid, they get the I'm sorry,
you can't come in message.
If we try it for Bob,
his password is valid,
he gets the welcome
message as expected.
But if his password is not
valid, he gets access denied.
Now, there is one
final thing of note
about this switch statement,
and that's that it doesn't
have a default condition.
And the reason it doesn't
is it doesn't need one.
It's already complete.
If we take a look at
the final case here,
this actually covers all
of the possible conditions
that we haven't already
matched above
and so the switch
statement is complete
without needing a default.
So those are some of the
basics of the Swift language.
I'd now like to hand over to my
colleague Brian to introduce you
I'd now like to hand over to my
colleague Brian to introduce you
to functions and
closures in Swift.
[ Applause ]
>> All right.
Thanks, Dave, as I was saying.
Let's get started by looking
at how you define a
function in Swift.
You define a function
using the func keyword
and you implement it
inside of the curly braces.
Here we've defined a simple
function called sendMessage
that prints a message
to the console.
And you call this message
in an intuitive way
by writing its name,
sendMessage,
followed by an empty
pair of parentheses.
So let's add a parameter
to the function
that indicates whether the
message should be sent shouting.
You write the parameter's
name, followed by colon
and the parameter's
type, just like you do
when you declare a
constant or a variable.
Here we've added a
parameter called shouting,
which is of type Bool.
And when you call the function,
the parameter's name
provides a label
for the argument you pass in.
Labeling your arguments
makes your code read better
Labeling your arguments
makes your code read better
and makes the purpose or
intent of each argument clear.
In this case, if you left
out the shouting label,
someone reading your
code later might think
that true just indicated whether
the message should be sent
at all.
So you can shout a message,
but who are you going
to send the message to.
Let's add another parameter
that lets us address the message
to someone in particular.
So here we've added a
parameter called recipient,
which is of type string,
and we've included
the recipient's name
in our message using
string interpolation.
And now when you call the
function, you can pass
in the recipient's name.
Now, although the message
prints as you'd expect,
it doesn't read very
well when you call it.
sendMessage recipient
Morgan is pretty awkward.
When you call a function, you
want it to read naturally.
In this case you'd like it to
say something like sendMessage
to Morgan, which forms a
nice grammatical phrase.
And you could do this
by changing the name
of the parameter, which
then changes the name
of the argument label, and this
does make the function read
better when you call it,
sendMessage to Morgan,
better when you call it,
sendMessage to Morgan,
but it doesn't work so well
inside the body of the function.
Inside the body you really
want a noun for the parameter,
not a preposition, hey
there to, isn't so great.
But sometimes there
isn't a single word
that works well both inside the
function's body and as a label
when you call the function.
In Swift you don't have
to give one of these up.
When a parameter's name
isn't appropriate as a label
for the argument
when you call it,
you can explicitly provide
a more suitable one.
You write the explicit
argument label in front
of the parameter's name.
Here we've added to as an
explicit argument label
in addition to the recipient
parameter, and this just means
that you can use to when
you call the function,
sendMessage to Morgan, and you
can still use recipient inside
the body of the function.
Let's add one more
parameter to the function
which lets you provide a custom
message when you call it.
Here we've added a message
parameter of type string.
Now when you call the function,
you can pass on your
own message.
Now, once again, this
code works as expected,
but it doesn't read
very well either.
sendMessage message
is redundant.
The message label isn't
helping to clarify the role
of the first argument
because it's already clear
from the base name of the
function, sendMessage.
Here the argument label actually
makes the code harder to read.
In the cases like these, you
can write an underscore in front
of the parameter's name.
And this means that you don't
provide a label for the argument
when you call the function.
And now our function reads
naturally when you call it;
sendMessage, See you
at the bash, to Morgan.
Now, it's not very often that
we need to shout our message,
so we usually pass in
false for that argument,
and in Swift you can actually
capture this behavior right
in the declaration
of the function.
Whenever a parameter has a
single commonly used value,
you can provide that value as
the default, and you do this
you can provide that value as
the default, and you do this
by assigning a default
value, in this case false,
right after the type
of the parameter,
and now when you call the
function, you can leave
out the corresponding argument
and the default value is used.
And when you're deciding
whether to use or when
to use an explicit argument
label, when to omit one or when
to provide default
values for parameters,
remember that functions
are declared only once,
but they're used repeatedly.
So the most important thing is
that the function reads clearly
and concisely when it's called.
Now, to learn much
more about the kinds
of things you should consider
when you're writing
great Swift API,
check out the Swift
API Guidelines talk.
So we've looked at lots of
ways functions can take values.
Let's take a look at some of
the ways they can return values.
Let's say you want
to write a function
that returns a first
string in an array
that has a given prefix.
The function takes a string
prefix, an array of strings,
and it returns the string
with a given prefix.
and it returns the string
with a given prefix.
As you can see, you use an
arrow to indicate what type
of values your function
can return.
In this case a string.
So let's see how it works.
First use a for-in loop to
iterate through each string
in the array and then you
check whether a string has a
given prefix.
You need to check whether the
string has a given prefix using
strings.hasprefix method.
If it does, you're done and you
can simply return the string
by writing it in a
return statement.
But what do you return
if the array doesn't contain
the string you're looking for?
Well, because this function was
declared to return a string,
the only option you have
is some valid string value,
in this case the empty string.
But this is not good Swift code.
However, as Dave showed
earlier, optionals are perfect
for representing values
that may be missing.
So you just need to change the
return type of this function
to be an optional string
by writing a question mark
after string, and now
you can return nil
when the string isn't found.
And because the function
returns an optional string,
you can call it safely
in an if-let statement.
Now, notice that Swift infers
a return type of the function,
so there's no need to
write it explicitly here.
So we've looked at some of
the ways functions can take
and return values of various
types, such as strings,
integers, arrays and
even optional types.
Let's take a look at writing
one more kind of function.
Let's say you want to write a
function that filters an array
of numbers based
on some condition.
Let's think about what
this function needs to do.
It's going to take in an array
of numbers and for each number
in the array it's going to
determine whether to include
that number in the
filtered results.
For example, if you wanted
an array that's been filtered
to contain only the
even numbers here,
you could test whether each
number is divisible by two.
In this case, of
course, four is even,
so it's included in
the result array.
If a number isn't
even, such as 17,
If a number isn't
even, such as 17,
the filtering function just
moves on to the next number
to test, and so on through
the rest of the array.
Now, let's look at writing
this function in code.
The function declaration
is what you might expect,
but what type do you put in for
the includeNumber parameter?
As you saw a few moments
ago, deciding whether
to include a number
in the filtered result is
itself actually a function,
not just a simple value,
like a string or a number,
and in Swift functions can take
other functions as parameters.
So what does the type
of a function look like.
Well, all function types in
Swift have this basic form.
The type of the function's
parameters, if any,
go inside of the parentheses,
followed by an arrow
and the function's return type.
So, for example, here's our
basic send message function
from earlier.
It has the type,
empty parentheses
because it doesn't take
any parameters, arrow Void.
Here void just means it
doesn't return any values.
And in Swift if your function
doesn't return anything,
And in Swift if your function
doesn't return anything,
you don't need to write
the arrow Void explicitly.
And here's the firstString
function.
Its signature is a
little more complicated,
but its type follows
the same basic form.
It takes a string,
an array of strings
and it returns an
optional string.
So now that you've seen this
syntax for function types,
it's pretty clear how you need
to finish the declaration
for filterInts.
The includeNumber
parameter can be any function
that takes an integer and
returns a Boolean, and you write
that type as Int in
parentheses, arrow Bool.
So let's go ahead and write
the rest of this function.
You need to build up an
array of filtered numbers,
so here's a variable
called result.
It's been initialized to
an empty array of Ints.
And as you iterate through
the array of numbers,
pass into the function, you need
to check whether each
number should be included
in the result array.
To do that you pass each number
to the includeNumber function,
To do that you pass each number
to the includeNumber function,
and notice that inside
the body of filterInts,
the includeNumber parameter
is treated as a name
of the function it's passed
in, and that just means
that you can call
it in the same way
that you do any other function.
So that's how you
write a function
that takes another
function as a parameter.
But how do you call one of
these kind of functions?
Well, first you're going
to need some values
to pass into filterInts.
So here's an array of numbers
and a simple function
called divisibleByTwo
that indicates whether
an integer is even.
As you can see, the type
of divisibleByTwo
matches exactly the type
of the includeNumber parameter.
So that means that we can pass
the divisibleByTwo function
as an argument to filterInts.
You do this by simply
passing in the name
of the divisibleByTwo function
and now you've got an array
of only the even numbers.
Now, notice that you don't
include the parentheses
when you pass in the
divisibleByTwo function,
and that's because you're
not calling it at this point.
and that's because you're
not calling it at this point.
Instead, it's called later
inside the body of filterInts.
Also notice that we passed in
the array of numbers by name,
but you could have passed
in a literal array instead;
for instance, if you just
needed to pass in a few values
without needing to create
a constant for later reuse.
The same is true for passing
in functions as arguments.
It's not very likely that the
divisibleByTwo function will
find much reuse.
And it would be pretty
cumbersome to have
to make a new function every
time you wanted to pass
in a different filterInt
condition.
And if you look at the important
parts of divisibleByTwo,
you can see that
giving it a name
at all is really just a
convenience geared toward reuse.
divisibleByTwo is just a
name for the functionality
that you see highlighted.
And in Swift, just as you
can write a literal string
or a literal array, you can
write a literal function
or a literal array, you can
write a literal function
without a name called
a closure expression
and pass it around in your code.
The syntax for writing a closure
expression is very similar
to a function declaration,
except that it doesn't
have a name.
You write the entire body
of the closure inside
of the curly braces and you
separate the closure's body
from its signature
using the Int keyword,
as in use the parameter
number of type Int in the body
of this closure,
which returns a Bool.
Now, this is the most
explicit or complete way
to write a closure, but
as you've seen before,
Swift can infer a
lot of information
from the context
your code appears in.
For example, the function type
of the closure is already known
from the type of the
includeNumber parameter,
and this means you don't
need to write it explicitly.
And when the entire body
of the closure is a single
return statement, as it is here,
you don't need to write
the return keyword either.
So this is much cleaner syntax,
but because the closure's
So this is much cleaner syntax,
but because the closure's
so short, even the number
parameter seems a bit redundant.
And in cases like these Swift
provides implicit argument
names, so there's no need
to even write the parameter
name or the Int keyword.
These implicit arguments
start with dollar sign
and a number beginning at 0.
So $0 refers to the first
argument to the closure,
$1 the second, and so on.
And even though these
are available to use
in any closure expression,
use them when doing
so doesn't make your
code harder to read.
Now, you've seen that Swift
provides lots of convenient ways
for writing closures,
but passing them directly inside
a function's parentheses is a
little punctuation heavy.
A closing curly brace right next
to a closing paren
is pretty terrible.
However, when the closure is the
last argument to the function,
as it is in this case,
you can write it instead
as a trailing closure right
outside of the parentheses.
And as you'll see later, if
the closure's the only argument
to a function, you can drop
the parentheses altogether.
Now, trailing closures
are especially nice
when the closure contains
more than one line of code.
For example, here's a
more complex closure
that determines whether the sum
of a number's digits is even.
Trailing closure syntax ensures
that even complex closures
with multiline functionality
read naturally and elegantly.
Now, the filterInts function is
pretty useful if all you want
to do is filter integers,
but what if you want
to filter arrays of other
types, like strings.
For example, say you
have an array of names
and you want a filtered array
that contains only the names
that have less than a
certain number of characters.
You could do this by writing
a filter string function
like the one you see here,
and since you've already
written a filterInts function,
let's see whether you can
reuse any of that logic.
Well, first, you'd obviously
want to do some bookkeeping
Well, first, you'd obviously
want to do some bookkeeping
and change all the
places that refer
to numbers to refer to strings.
And next you need to change all
of the Int types
to be string types.
And because the behavior
of these two functions
is actually the same
for both integers and strings,
there's really nothing
else to do.
They're essentially
the same function.
And now you have this function
that filters an array
of strings.
Now, if you want a
filtered function that works
for other kinds of types, you
can repeat this process over
and over and over again
for each kind of type,
but this would obviously
get boring pretty quickly.
So instead, you can write
one function that works
with arrays of any kind of type.
This is called a
generic function.
So let's look at
how you write one.
Writing one is not
much different
than what you've seen already.
First you change the
names to be more generic.
So, for example, the function's
name is simply filter,
rather than filterInts
or filterStrings.
rather than filterInts
or filterStrings.
Next, and more importantly, you
need to fill in the placeholders
that you see here with
the type of the elements,
but you don't know
what that type is yet.
So instead, what you really
want is a type parameter.
The actual type is determined
when the function is called.
Here we filled in
the placeholders
with a generic type
parameter called element.
And so that you can
tell the difference
between type parameters and
actual pre existing types
like strings or integers,
you write the type parameters
up front inside of the
angle brackets right
after the function's name.
And now you have a generic
filter function that works
with an array that
contains any type of values.
You call this kind of function
in the same way as before.
You don't need to specify the
type you want the function
to work with because
it's all inferred
from the values you pass in.
Filtering is such
a useful operation
that the Swift standard library
has a filter method that works
that the Swift standard library
has a filter method that works
with any kind of
sequence or collection.
It works in basically
the same way
as the filter function you just
saw, except that it's a method
so you call it using
dot notation,
names.filter, as you see here.
The Swift standard
library actually has lots
of other methods that take
closures as arguments.
And another really useful
one is the map method,
and it also works with any
sequence or collection.
Map returns a new array
that contains the result
of a plan enclosure
to each element.
So here's an example that
calls map on the array
of short names from above.
It passes a closure that
simply upper cases each string.
You can even chain
these together
to perform multiple operations.
So, for example, you can
perform the filtering
and the upper casing
in one expression.
By chaining these two
method calls together using
dot notation.
The filtering happens first,
and then map is called
on the filtered array,
which then finally returns
the upper cased strings.
And this reads nicely even
when you're using the
trailing closure syntax.
So the combination of closures
and functions that take them
as arguments makes it possible
to write really complex,
powerful code in a concise
yet highly expressive way.
So we've looked at
functions and closures,
and even a bit of
generic functions.
I'd like to invite Alex
on stage to tell you all
about data types in Swift.
[ Applause ]
>> Thanks, Brian.
You've seen how to use
existing data types in Swift.
Now let's take a look at how
to create some data
types of our own.
Let's start with structures.
You create one like this.
You write the struct
keyword followed
by the name of a structure.
Here, rectangle.
Then inside the curly braces
you write the properties
Then inside the curly braces
you write the properties
of the structure.
Properties use the same
syntax you've seen before
for variables and constants.
Both properties here have
the value assigned as part
of the declaration, so
you can create a rectangle
by just writing rectangle,
the name,
followed by a pair
of parentheses.
And to access the properties,
you use dot notation.
You don't have to provide a
value for a property as part
of the types declaration.
In fact, most of
the time you don't.
More often, you write just the
type and then you set the value
when you create the instance.
So another property
of a rectangle besides its
dimensions is its area.
You wouldn't want to
store the area though.
You'd have to update it every
time the dimensions changed.
What you want is a property
whose value is calculated right
What you want is a property
whose value is calculated right
at the point you need
it and you can do
that using a computed property.
To make a computed property,
after the property's name
and its type, you write a
function body that's responsible
for computing and returning the
current value of the function.
Even though it's computed
rather than being stored,
you still access it
using dot notation.
There's more that you
can do with properties.
You can have setters and
observers, and you can find
out information about those
in the Properties chapter
of the Swift Programming
Language.
Just like you can define
properties on a structure,
you can also define methods.
They use the same syntax you've
seen already for functions.
And just like properties, you
access them using dot notation
And just like properties, you
access them using dot notation
when you need to call them.
You've seen this syntax a few
times to create a rectangle.
Let's look at what it does
in a little bit more detail.
It looks a little bit
like a function call
because of the parenthesis,
but it's actually
calling an initializer.
So far we've been using a
special initializer called a
member wise initializer
that Swift implements
for you on structures.
Here's what it would
look like if you write
that initializer out explicitly.
You write the init keyword and
then inside you set a value
for the rectangle's properties.
Notice that there are two
things here called width.
There's a property and there's
a parameter, and you write self.
to explicitly refer
to the property.
to explicitly refer
to the property.
So that's how you write
your own initializer.
There's enough code in
this rectangle structure
that it could benefit from some
organization, and one way to do
that in Swift is
using extensions.
An extension lets
you add functionality
to an existing type.
In Swift you can use extensions
to divide up your code.
Core functionality can go in
the structure's declaration
and additional functionality can
go in one or more extensions.
You're not limited to
extending your own types.
If you need to, you can extend
types from other places,
such as foundation or
the standard library.
You saw earlier how you
can make a generic function
which performs the
same operations
on data of different types.
You can also make
a generic structure
which contains data
of different types.
The syntax looks very similar.
You still have the generic type
parameters inside angle brackets
after the name.
This example attaches
a name to an array
of some kind of element.
This would be a useful data
structure, for example,
to populate a table view that
has sections with heading names.
You create a generic
structure instance just
like an instance
of another type.
Here you can see if you
write the type annotations,
board games and primes
have different types.
One is an array of strings
and the other is an
array of integers.
They have different
element types
so they are different types.
You don't have to
write the arrays.
Swift infers what
element is automatically.
So that's structures.
Another data type
in Swift is classes.
You write class before
the name, but everything
about structures still applies.
Properties, methods,
initializers, and so on.
So why would you want
to create a class?
One reason is that your code
can refer to the same instance
of a class from several
different places,
and that's different than
the behavior you have
for structures.
A structure acts
like one big value.
For example, in a game, if
you make a score for player1,
and then give player2
the same score,
you have two independent scores.
Changing player2's score
doesn't change player1's score.
Changing player2's score
doesn't change player1's score.
However, both players need to
refer to the same file on disk
when they log their high scores.
Since it's a class, that's
the behavior that you get.
When player2 gets a new
high score and then logs it
to the file, both
players see the change.
If you're coming from another
object oriented language,
you might be used to
writing a lot of classes,
and in Swift you don't
need to write them
as often as you might think.
Anytime it makes sense to check
for equality or to make a copy,
you usually want a value
type like a structure.
For more information about how
and why to use value types,
there's a great talk
you can check out.
Another reason to use
classes is when you need
Another reason to use
classes is when you need
to subclass an existing class.
For example, suppose
you have a framework
that gives you this
fish class with sort
of core fish functionality.
You can subclass it
to add functionality,
like a FlyingFish can
add a flying method.
You indicate that this is a
subclass by writing colon fish.
Subclasses can also
override a method
to provide their
own implementation.
Like this ComplainingFish.
It swims like a normal fish, it
just complains about it first.
You write super.swim to call
the superclass' implementation.
This code doesn't compile yet.
When you override
a method in Swift,
you have to mark it
explicitly by writing override,
you have to mark it
explicitly by writing override,
and now this code works.
Just like it's an error to
override something by accident,
it's also an error
to write override
when you don't override
anything.
That means if you misspell a
method name that you're trying
to override, Swift tells you
about the error right
at compile time.
A segue can also
provide an initializer.
For example, fish that has an
initializer that takes a name.
ComplainingFish needs
an initializer
with both a name
and a complaint.
Inside the initializer
you set the initial value
for the properties
declared by the subclass,
and then you call super.init
to let the superclass
finish the initialization.
There's more you can
do with initializers,
There's more you can
do with initializers,
especially around classes.
You can read all about it
in the Initializers chapter
of the Swift Programming
Language.
You've seen how you
can use subclasses.
Now let's talk about
a subclassing problem.
Continuing the game example,
suppose you have this
player base class.
Since every player
can take a turn,
there's a method to do that.
You have two kinds of players.
You have a HumanPlayer
class, which takes its turn
by showing UI to the user.
And you have a ComputerPlayer
class which takes its turn
by finding the best legal move.
So the question then
is what goes
in the base class
implementation?
Well, there's no shared behavior
between human and
computer players.
between human and
computer players.
There isn't any shared
code to factor out.
And in the surrounding code you
would never want an instance
of the player base class.
So this method should
never be called.
The only reasonable thing you
could write here is some sort
of fatal error to help
you catch mistakes early
in the development process.
All the player class is doing
is describing what it means
to be a player, that every
player can take a turn.
It's a sort of blueprint
for players.
In Swift you express this kind
of relationship using
a protocol.
Protocols specify requirements
like methods and properties.
They're like interfaces
or abstract classes
that you might know
from other languages.
You make one using
the protocol keyword.
You make one using
the protocol keyword.
And because it describes
requirements for other types
to fulfill, you don't
provide an implementation.
Types conform to a protocol by
providing that implementation
and you declare conformance
by writing : player,
just like you do when
there's a superclass.
At this point you're
not subclassing anymore
so these methods are
not overriding anything.
So you don't write override.
There's no real reason these
need to be classes anymore,
so let's make them structures.
And let's take a closer
look at HumanPlayer.
It has a few other properties,
like a name and a score.
And if you make an instance of
player, you can print it out.
And if you make an instance of
player, you can print it out.
You get a default
description here
because the standard
library has a conversion
from any type to a string.
But what if you wanted to
customize the conversion
to print a nicer description?
The standard library also
has a protocol called
CustomStringConvertible
which lets you provide a custom
description, and here's what
that protocol looks like.
It has one requirement --
a description property.
Any type that conforms
to this protocol uses the
custom description.
Okay. So where should
you implement the
description property.
Remember from earlier how you
can organize your code using
extensions with core
functionality
in type declarations,
and additional functionality
in extensions.
Having a custom string
conversion definitely falls
Having a custom string
conversion definitely falls
into the second category.
It's not core functionality, so
let's put it in an extension.
Here's how you extend a type
to add protocol conformance.
You write colon
CustomStringConvertible
in the first line.
And then you implement the
requirements inside the
extension body.
Now when you call print,
you get the customized
string conversion.
There's a lot that you can
do in Swift using protocols
and extensions to organize your
code and create abstractions.
For more information
check out this talk
on Protocol Oriented
Programming from 2015.
That brings us to the last data
type in Swift -- enumerations.
You use an enumeration when
there's a list of values
that you know ahead of time.
Here's an enumeration
that supports left
and right alignment for text.
When you use an enumeration,
you use dot notation
to access its cases.
Here there are only two
cases, so it's still readable
if you write them on one line.
Because an enumeration has
a list of possible values,
it's very common to use one
with a switch, one switch case
for each enumeration case.
Writing alignment over and
over is a little repetitive
and here it's not necessary.
The switch is considering
textAlignment,
so the only possible
values to check
for are enumeration cases
from that alignment.
That means you can omit
the enumeration name
and write just .left.
Also, notice that there's
no default case here.
Also, notice that there's
no default case here.
That's for the same
reason you saw earlier.
The switch already covers
every possible alignment value,
so there's nothing left for
a default case to handle.
Omitting the default case
has a nice advantage.
If you come back later and
add a new enumeration case,
but forget to update the switch,
Swift will highlight the error
until you add the missing code.
Now let's take a quick look
at two more things you
can do with enumerations.
You can associate values
with each enumeration case.
For example, the alignment can
specify how much padding to use
and you can get that
padding value back
out as part of the switch case.
You can also give each
enumeration case a raw value
from some other type, such
as a string or an integer,
which lets you improve
type safety in your code
by using enumerations
instead of string constants.
You've seen a lot of Swift this
afternoon, but there isn't time
to show you everything.
One last thing I'd like to
show you is error handling.
In Swift you can use an
enumeration to describe errors.
You use throws to
mark a function
that can throw an error.
You write defer before a block
of code that must be executed,
whether or not there's an error,
and that execution happens
just before exiting scope,
such as returning
from a function.
such as returning
from a function.
Before calling code that
might throw an error,
you mark it with try,
and you can catch
and throw errors
using catch and throw.
For all the details
about error handling,
take a look at the
Error Handling chapter
in the Swift Programming
Language.
You can find a link to that
book and other resources here.
There are lots of other
great Swift talks to check
out later this week or on video.
Thank you.
[ Applause ]