WWDC2015 Session 403

Transcript

X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
[ Applause ]
>> WOODY LIDSTONE: Hi, everyone.
I made a comment to Stefan
earlier today, saying,
I hope some people
come out to this.
And it's incredible.
You are just packing the place.
So, thank you for coming out.
My name is Woody, and I
would like to introduce you
to Improving Your
Existing Apps with Swift.
Can I get a show of hands,
how many of you are
actually using Swift
in your code right now?
Okay. Excellent.
Then you are all at
the right session,
whether you've done it or not.
Hopefully by the end of it
you will all be using Swift
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
with your existing
Objective-C projects.
You know, last year I was also
in this room, I was sitting
in the back corner as an
attendee, using my laptop,
trying to figure out how to
use some of the technologies
that I learned about
in an earlier session,
replying to work email even
though I told them I wouldn't,
and half paying attention to
the presenter that was up here,
as I am sure many
of us are right now.
Well, since then,
I've switched sides
from the attendee to the stage.
I have moved sides of the
continent from Halifax,
Nova Scotia, to California,
and I am out today
to help you learn some
techniques to work
with Swift and Objective-C.
I don't have to go
over and move it.
I've got a remote control now.
So what I want to do is
take you through some
of the techniques you can use to
add Swift to an existing project
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
of the techniques you can use to
add Swift to an existing project
to make that project better.
And I thought the approach
I would use would be
to do exactly that
in this session.
So I went to Infinite Loop, I
went down to the parking garage,
and down to a floor below that
where we have our catacombs,
and I went past the three-headed
dog that guards the entrance
to industrial design, to the end
where we keep our
software vault.
And I opened the
software vault and I took
out an app called The Elements
that we haven't seen at WWDC
for maybe two or three years.
I'd like to show you
how it is right now.
Alright, this is The Elements.
Do you remember seeing
this before?
Yeah? Some of you do,
some of you don't.
Somebody really like it.
I like it too.
So it's a standard
UIKit-based table view app.
We have got a bunch
of cells for each
of the elements in
the atomic table.
We have this small,
little detail view
with a little bit
of information.
And if you tap on
one of the cells,
we have this navigation
controllers push presentation
to show some details about that.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
to show some details about that.
Great. I figure it's something
that probably a lot
of you have to do.
You move around between
different applications,
and you come back and
you revisit an app
that you haven't used in a
while or you haven't programmed
in a while and you are asked
to add new features to it
or support a new
operating system, a new SDK.
So that's exactly what
we are going to do.
We are going to modernize
this Objective-C app
and we're going to
do it using Swift.
The pitch to you is this: if
you have an existing application
and you have got to
write new code for it
and that existing application
is an Objective-C app,
consider taking advantage of
Swift's features by writing
that new code in Swift,
while, at the same time,
leave your original
Objective-C code in Objective-C.
That's perfectly fine too.
In this session, we are going
to start by doing a makeover
on the application to
bring its user interface
to a more contemporary
appearance.
We are going to do
that using Swift.
Then we are also going to
take a look at, for example,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Then we are also going to
take a look at, for example,
Swift's structs and its
functions like mapping
and reducing to find ways
that we can add new
features to the application.
This application has small
tiles and large tiles.
The small tile is
the table view,
the large tile is
the detail view.
And the tiles consist
of a background,
plus the text that's rendered
on top of that background.
Because this app
is an older app,
the backgrounds were originally
rendered in Photoshop,
and then they are just
embedded inside the application.
But these backgrounds
which exist
in different colors depending
on the atomic states, solid,
liquid, synthetics,
gaseous, are only rendered
for the original iPhone.
There is no Retina
artwork included.
Which means when we take
this older app and we run it
on newer hardware, we have to
scale up, and when we scale
up this older, this
older artwork,
we end up getting these
artifacts, called aliasing,
on the rounded corners because
we don't have enough pixel data
to represent those
corners smoothly.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
to represent those
corners smoothly.
So that's something that we
definitely are going to fix
up in this presentation.
The other thing is,
do you ever look
at your middle school yearbook,
maybe in the '80s
or '90s [applause]?
Yeah, right?
You see a picture of yourself,
and you are there
wearing a vest with, like,
this embroidered kitten on it,
and you are holding a keyboard
because it was cool at the time,
and you've got this
laser background
because that was also cool.
And now you are looking at it
and you are just thinking,
what was I thinking?
[ Laughter ]
Sometimes we look back
at our apps and we think,
what were we thinking?
With the shine and the
gloss and the reflection.
So we are going to fix that.
Fashions change.
Styles change.
And one of the ways
that we can do
that with this app is
simply removing the gloss
on the background.
We get a very contemporary,
rendered rectangle outline.
It looks good in the
small tile as well
as in the large tile
for this app.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Now, we could rerender the
new backgrounds in Photoshop,
and embed them inside
the application,
but clearly this is
an app that we don't,
we don't get into very often.
You know, it might not be until
WWDC 2020 before we come back
to The Elements, and
I'd like to make sure
that it looks good not
just on past hardware
and current hardware, but also
on potential future hardware.
So I am going to choose to put
in some custom drawing code just
to draw the rounded
rectangle background
because it's just a
rounded rectangle.
I am going to do that
in Swift, but I am going
to call it from Objective-C.
To do that, the technique I
am going to use is called Mix
and Match, and we have
numerous sessions both last year
and this year on
interoperability between Swift
and Objective-C, the mechanics
of which can be covered
better in those sessions.
What I would like to do at
this point is just give you an
overview of how the
technology works
and then we'll actually dive
into a demo and you can see it.
Typically when we think
about classes in Objective-C,
we have a header file and
an implementation file,
and those two things together
form our class definition.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
and those two things together
form our class definition.
But it's possible
that we can have some
of our methods implemented
in a category,
and that's perfectly fine,
and then our class definition
is our base class plus
our category.
And there's nothing to say
that you can't have
multiple categories.
In fact, this is something
that we do with UI table view.
If you look at this header file
in Objective-C, you will see
that there's many
categories on UI table view.
Now, there's also
nothing to say that one
of those categories couldn't
be implemented in Swift,
in which case the
terminology changes
but the concept is the same.
We just call it a Swift
extension on the Objective-C,
in this case, base class.
And there's also nothing
to say you couldn't have
multiple Objective-C categories
mixed in with multiple Swift
extensions and the content
of all of them forms your class.
This lets us have some
of our functions written
in Objective-C, and it lets you
have newer functions be written
in Swift.
For this interoperability
technique to work, we use a set
of bridging headers -- not
a set of bridging headers --
we use a bridging header
and a generated header.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
we use a bridging header
and a generated header.
Now, the bridging
header is made in Xcode.
The first time that
you introduce Swift
into an existing
Objective-C project.
And then you maintain it.
You just basically go into it,
and put some import statements
so you can selectively
choose which data types
and header files
from Objective-C you are
going to expose to Swift.
Then on the reciprocal,
the Swift compiler
makes a generated header
which you can import into your
Objective-C implementation files
to expose Swift, in
this case, extensions
and other data types,
to Objective-C.
So we have these
two header files.
You are going to see them
in the demonstration,
which is coming up right now.
Okay. So in my project,
I have this class called
atomic element tile view.
That's the one that actually
draws the background.
There is a method in it.
It's actually not just
drawing the background,
it's drawing the background and
all the text that goes on top
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
it's drawing the background and
all the text that goes on top
of it for both the small
tile and the large tile.
So I am going to introduce the
new drawing code, but I am going
to do it in Swift, so I will go
ahead and make a new Swift file.
It's command-N in Xcode.
We will choose a
Swift file, create it,
and because it's the first
time that I've added Swift
to this project,
I have the option
to create the bridging
header now.
I want to create
it, so press Return.
And now I have my two files
that consist of the base class
in Objective-C plus
the Swift file.
I also have the bridging
header down here.
Let's just make this
a little bit wider.
There we go.
You might notice that I chose
to use the exact same file name
for my Swift file as I
did for the Objective-C.
That's not a requirement,
but it's a convenience
because it means that in
Xcode, I can use the shortcut
of control-command-up arrow and
just cycle through the Swift
and the Objective-C
header and implementation,
just quickly move back and
forth between all three.
Now, the bridging
header is where I choose
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Now, the bridging
header is where I choose
to expose data types that
I've declared in Objective-C
to Swift, and I do that by
importing their header files.
Since I want to extend atomic
element tile view into Swift
or using Swift, I have to import
it into the bridging header.
The only one that I really
need at the moment is the one
that I've highlighted,
atomic element tile view,
but later on in the presentation
I will need the other ones,
so I am just going to go ahead
and get them all imported now.
Now we can go over
to the Swift file,
and I will write an extension
on atomic element tile view.
So now the class atomic element
tile view has this new function
called draw raw background.
And you can see I am also
using the new Xcode 7 markup
to give a document comment,
a documentation comment,
in this case, draw an atomic
elements background tile.
I will go ahead and put the code
in that actually
does the drawing.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
And that's it for
the Swift part.
But now I want to call this
Swift function from Objective-C
so that instead of having
the pre-rendered images being
loaded, I am going to
draw using this method.
So I'll switch over to
atomic element tile view.m,
and I want to make sure that it
sees the code that I just added
in Swift, so I will go ahead
and import the generated header.
Generated header uses the
same name as the product,
so the elements, then you
append hyphen Swift.h. And now,
in this method, where
I typically loaded the
pre-rendered backgrounds,
I'll comment that out.
And instead call 'self draw,'
and you can see that the method
from Swift is showing
up as a native method
with everything else
that's there.
You can even see the
comment is showing
up 'draw an atomic
elements background tile.'
So I will pass over the element.
Pass it to bounding rectangle
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Pass it to bounding rectangle
in which it will draw
the rounded rectangle.
Run the app.
There we go.
So we are having some rounded
rectangles being drawn using
Swift code with Objective-C
[applause].
Thank you.
It's the benefit of planting
your friends in the audience.
[ Laughter ]
Now for the most part, it
looks kind of like I expected.
It's a rounded rectangle.
But the rounded rectangles
themselves,
they don't look the
way I intended.
In fact, if we zoom in on it,
you can see that it's rounded
on the inside but it's not
rounded on the outside,
and that wasn't the intent.
I wanted pure rounded,
pure roundedness
on both the inside
and the outside.
So let's explore in more
detail why that's happening
as we take a look
at Swift structures.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
as we take a look
at Swift structures.
And how Swift makes it easier
and more natural to
work with structs.
So we have a lot of primitive
structs, for example,
in the Core Graphics framework.
We have things like CGrects,
CGpoints, CGsize, and so forth.
And what we did when we drew
is I have a bounding rectangle,
which is a CGrect, and
I drew this bezier path.
Now, the bezier path
is the grey outline
on the screen, drew
the bezier path.
Next it was put inside of
the bounding rectangle,
and you can see the
bounding rectangle here.
Bezier paths themselves are
not something that you see.
They don't render until
you apply a line to them,
like a line stroke, and
then that's what you see,
that's what's rendered
against the bezier path.
So we draw the bezier path.
We then put a stroke on it.
The stroke is, for example, ten
units wide, ten points wide,
but it's going outside
the boundaries
of the bounding rectangle.
And that results in clipping.
So the rounded rectangle is
actually there on the outside,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
So the rounded rectangle is
actually there on the outside,
but this clipping
that's happening
with the bounding rectangle is
preventing it from being there.
I know some of you in the
audience might think, well,
if you have an issue
with clipping,
just turn clipping off, and
then that issue is fixed
and you go on to the next thing.
Which is, I suppose, true.
It's kind of like solving
issues with Swift, though,
in Xcode by just shuffling
around Swift's exclamation marks
until it compiles [laughter].
You can do it, but I would
never call that a best practice.
And when it comes to clipping,
there's a performance
issue with this.
With every API that you
call, with every task
that you initiate, with
every move that you make
and every breath that
you take - [laughter] --
you need to consider the impact
on power and performance.
And it's not very performant
to be constantly calculating the
intersection of two rectangles
and performing clipping on it.
It's more performant
just to set the rectangle
to be the right size
in the first place.
That's what we are going to do.
We are just going to
inset the bezier path
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
We are just going to
inset the bezier path
so that it doesn't clip itself.
Now, to do that, we are
going to use some methods --
and I do mean methods,
available on CGrect,
when it's being used in Swift.
Think about how these primitive
types are used normally.
We have things like CGrect
and CGpoint, and so forth,
as I said, and they
might exist over here.
And then separately
you have got the set
of global utility functions that
work on them, like CGrectZero,
CGrectMake, or GetWidth,
or some actual functions
like get the union
or the intersection.
There's this cognitive
separation between the two.
We know that this is the
type, and then we have to know
that these are the
methods -- or sorry --
the functions that act upon it.
Well, when we work in Swift,
we actually change how things
like CGrect and CGpoint
and CGsize come into Swift.
We basically use encapsulation
and take all those,
those global, formerly
global functions that can act
on that structure and build
it into the structure,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
which makes it much easier
to have code completion,
makes it more awesome to
predict what the API is,
because you can just
do my struct dot
and then you get code
completion for all the methods
and properties that it has.
But if we left the
names like this,
it wouldn't really feel native.
So the names are actually
remapped to have a feel
to make them first-class
methods on these data types.
The benefit of this is the way
that you work with structures
in Swift, calling functions
or calling methods really
or accessing their
properties, exactly the same way
that you work with classes.
It's exactly the same way that
you work with enums, or enums.
It's all the same
consistent style.
We also get to use
initializers that are the same
across all the different
data types.
We get to have better
code completion.
So all in all, working with
these types is much more natural
in Swift because they behave
as first-class data
types with methods.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
as first-class data
types with methods.
Okay. The other thing
that we are going to do
in the following demonstration
is, I don't know about you,
but when I work with
graphics code,
especially some Core Graphics
stuff, one of the approaches
that I would use is, I
would render the code,
and then I would run it, and
then it builds and it copies
into the simulator, then
I navigate to that part
in the simulator where the
code is actually activated,
where it's used.
Then I inspect it, and if
I don't like it, I go back
and I tweak the code and
I run the whole thing.
This is a -- this loop,
this round-trip can be
really expensive time-wise.
Just to see what it looks like
when you change a line width
from three to four or you
turn off rasterization
or you do some sort of
setting change as you try
to get things to work.
So there's a better approach
that doesn't have
this round-tripping,
and it doesn't have you
often commenting out portions
of your code just to
experiment with how it looks.
If you really want
to experiment,
and play around with some code,
and see how it, how it works,
that's what we have
playgrounds for.
With playgrounds, this
loop gets switched
to just something like this.
You just tweak the code, and
you see the change right away,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
You just tweak the code, and
you see the change right away,
and if you don't like it,
just change it right away.
There's none of that copying
to the simulator, navigate
and see what it looks like.
So we are going to
fix up the problem
with our rounded
rectangles, using a playground
and using the methods
that are part of CGrect
to get a better CGrect
out of it.
Let me show you.
The drawing code is over
in atomic element tile view
dot Swift.
That's here.
And this is the function
that I just copied
in the previous demonstration.
It's the one I want to play
with, so I am just going
to copy it into a playground.
So I've copied it,
I'll hit command-N
and make a new iOS playground.
Paste it in.
And now I have the
drawing function.
No developer is an island.
No drawing functions
live in isolation either.
Drawing functions have to
draw in the context of --
well, a drawing context
or graphics context.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
well, a drawing context
or graphics context.
And the easiest way to get
a graphics context is just
to make a subclass of UIView.
So that's exactly
what I am going to do.
I am just going to declare
a subclass of UIView here.
All it does is call
that drawing function,
and then I'll instantiate
that class here.
And then I can take the image
that's drawn as a result
of my drawing function
and just add it directly
to the storyboard here.
Make it larger so we can see it.
And then you can
experiment with it.
You can kind of figure
out what you need to do
to make it draw the way
you want it to draw.
So, for example, I
want to see if it looks
like if it has only
120 points across.
There's the result right away.
Maybe I want to see
what it looks
like if the line width
isn't 6 but is actually 60,
with a corner radius of 356.
That's what we get.
So you can keep experimenting
with it.
And then once you have the code
working the way that you like,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
And then once you have the code
working the way that you like,
you just copy and paste it back
into the actual file
from which it came.
So in this case, I want to have
my line width scale so that
if it's the small tile
view in the table view,
it's got thin edges, and
if it's the larger one,
it has proportionately
thicker edges.
So I am going to make
it related to the width
of the background rectangle.
So background rectangle.width
divided by, in this case, 36.
And I will do the same
thing for the corner radius.
Backgroundrectangle.width
divided
by something smaller, like 16.
So now it's starting to
look like I want it to look,
but I still have the
problem where I am clipping,
so I am not able to
see the full extent
of the stroke nor the
rounded rectangles.
So, to do that, on
background rectangle,
I will use the method
called rect by insetting,
and I will inset it to
be half the line width.
So that's line width
divided by 2 and same thing,
line width divided by 2.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
line width divided by 2.
And there, now I have a
perfectly rounded rectangle
exactly like I expected to
have in the first place.
I will copy this code from
here back to the extension,
replacing the file -- sorry --
replacing the method
that's there.
Rerun the app with command-R.
And that's exactly what I wanted
to have, rounded rectangles.
Yeah, please.
Yeah.
[ Applause ]
Yeah, pretty much at
WWDC, if ever you feel
like applauding, just applaud.
Nobody is going to
say stop that.
It's all good [applause].
See, thank you.
So the app is looking okay.
I've got these rounded
rectangles.
That's great, that's
what I want.
But the next thing I want
to do is make this app feel
more like a current app.
Now, I don't know about
you, but have you ever gone
to your client after
especially WWDC and said, hey,
there's this new version
of iOS coming out,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
there's this new version
of iOS coming out,
for example, iOS
9 is coming out.
I think we should just
not support iOS 8 anymore,
and we are only going to support
the new upcoming operating
system [applause].
Yeah. Have you done
that [applause]?
Yeah, and then you are looking
for another client [laughter].
Because they all want you to
support these old versions,
iOS 7, 8, and now 9 is
probably going to be on slate
for this fall for many of you.
So to do that, we have this new
availability feature introduced
in Swift 2.0.
So as long as you are writing
some code in Swift 2.0,
we have a great way to check
to see what SDK you are on
and if you can actually
use this feature or not.
Now, last year at WWDC,
we introduced some new view
controller presentation APIs
that allow for popover
presentations on iPhone.
So what I want to do in my
elements app is when I am
on any device that supports it
-- for example, an iOS 8 device
or newer -- I want to use
a popover presentation.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
or newer -- I want to use
a popover presentation.
But when I am on iOS 7,
I want to continue using
the navigation push
presentation style.
All right.
So how do we do this?
Well, this is the classic
way of checking to see
if you support an API.
We do whatever the type
is, we check and see
if it responds to selector.
And if so, then we
just use the selector.
If not, then we do
something else.
This is the way that we do
it in Swift, as of Swift 2.0.
We have this hashtag available,
and you specify the SDK
that you want, so iOS 8.3 in
this case, and then if so,
we use a popover, and if not,
we use the other approach.
And the benefit of this style
is that you are not waiting
to runtime to see if
it actually works.
At compilation time, the
compiler can tell you, yes,
this will work, or it
won't work depending
on your deployment target.
So if I am deploying to iOS
7, it's able to tell me,
popover isn't available.
But because I've
properly guarded it
by giving it another path,
it's able to compile.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
In this case, if I hadn't put
that guard in, if I hadn't said,
here's the check and I tried to
compile this and it was trying
to deploy back to
iOS 7, it would say,
popover presentation
controller is not available.
It's only available
on 8 or newer.
And then it gives
you some fixes.
But instead of just showing
you some more slides,
let me actually show
it to you in code.
Alright. So first of all,
the code that presents the
second view controller is kept
over in
elementsviewcontroller.m. It's
just an implementation of table
view accessory but in tapped
for row with index path.
And because I want to use
availability checking,
I have to have this implemented
in Swift, not Objective-C.
So I am just going
to comment it out.
And then I am going to
create a class extension much
like the original demo
from 20 minutes or so ago,
so that I can extend elements
view controller and have some
of its functionality
implemented in Swift.
So I will hit command-N to make
a new file, it's a Swift file.
It's elements view
controller dot Swift.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Then I will write a class
extension on it here.
Extension, elements
view controller,
and inside of there I will
just take the equivalent
Swift function.
So this is essentially
the same code
that I already had
there in Swift.
There's nothing new
at this point.
It's just the same thing
now implemented in Swift,
but this lets me bring up
the availability checking.
This particular application
is targeting iOS 7.1,
and I want to use this new
popover presentation controller
technique in it, so I am going
to comment out this line and put
in code that tries to directly
call the popover presentation.
So I am getting some errors.
One of the errors it's
complaining about is
that I don't actually conform
to the delegate protocol
that I need to for popovers.
Well, just on the aside, it's
okay to take an extension
in Swift and use it
to add conformance
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
in Swift and use it
to add conformance
to additional protocols.
So I'll just add UI popover
presentation controller
delegate there.
That clears up that error,
but I still have the problem
about trying to use an API
that doesn't exist on iOS 7.1,
which is exactly
what I wanted --
well, in the demonstration
anyway.
So I have a couple
of ways to fix it.
I have it pre-baked
for you here.
To say if I am running on
-- in this case, iOS 8.3 --
go ahead and display
as a popover.
Otherwise, use navigation
controller.
Now, if I run it and I tap
on the i -- let's see here.
We get a popover.
It's been 15 years and we
are still getting Carbon
at WWDC [applause].
Alright.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
For more information about
availability checking,
please check out Thursday's
session called Swift
in Practice.
There's a whole big
talk about it.
Next up, the previous
demonstrations were
about improving and modernizing
the look of the application.
You know, we changed the
drawing to get rid of the gloss.
We now have a popover
and so forth.
But let's actually add
some functionality to it.
So we are going to
implement live searching.
And to do that, we are
going to take a look
at Swift's filter method,
which is now available
on all collections,
including sets and arrays.
Filtering can be used to
drive a live search function
where only atomic elements
that match the search string
are displayed in the table view.
A standard setup for this would
be, well, something like this.
We've got a TableView.
The TableView is paired
with a view controller
that acts as its data source.
That view controller
has an array of content.
And when I type something
into the search field,
I have a delegate
method that's called,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
I have a delegate
method that's called,
on the view controller called
searchbar:textDidChange.
I figure out the string
that's been passed.
It's just an argument.
I filter the array of content.
And then I tell the
TableView to update
with this now filtered
array of content.
Search bar:textDidChange
looks something like this.
In actuality, it looks
exactly like this
because that's the
code that I am using.
Now I want to draw
your attention
to the highlighted
section, which is a closure.
So I am running a
filter, and in the filter,
it's going to be applied
to every one of the items
in the array, and that item in
the array, I have a placeholder,
which the dollar sign zero,
I am getting its name,
and then I'm asking
if it has the prefix,
and whatever the
search string is.
It kind of works like this.
Here I have my original
array up at the top.
And I have a closure for filter.
And it just cycles through,
passing in the element each
time, and if it has the name
that begins with the
letter, in this case N,
it's passed through to the
returning filtered array.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
And if not, it's not.
Let me show you this in code.
But just to speed things
along, I've already gone
and put a search
bar up at the top.
But I haven't rigged it up yet,
so if I try to do
anything inside of it,
it just doesn't work because I
haven't put the delegate method
in yet.
The delegate method is this.
We are saying, if the
search text is empty,
show all the atomic elements.
That's here.
If it's not empty, I
want to do a filter,
and that part is
left to be done.
Let's do it right now.
So I am going to
bring up a filter.
And if it's the first time
that you've seen closures,
great, welcome to closures.
Let me show you a
little bit about it.
And if not, maybe it's
a bit of a review.
When Xcode shows you these blue
kind of tokenized backgrounds
that are all singular items,
you can double-click them,
and they expand out, and
you just fill in the blanks.
So I'll take this closure,
double-click it, it expands out.
And I know that I have an
array of atomic elements.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
And I know that I have an
array of atomic elements.
The data type is
called atomic element.
So the parameter that's being
passed in is one atomic element.
So I am just going to
specify that in this closure,
the parameters that
are being passed
in is one atomic element -- I am
going to give it a local name,
so atomic element
is its local name,
and its data type
is atomic element.
Inside the closure, I need
to check to see if I want
to include it in the
filtered results or not
because I promised to give a
return value that's a Boolean.
So I'll say my return value
is whether the atomic element,
name, has prefix, and then
the search text that's passed
in from the search bar.
Like that.
Now, when I run it, we'll
do a search for everything
that begins with letter N.
There we go.
We get it filtered
using Swift's filter.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
We get it filtered
using Swift's filter.
But there's two things
I want to show you.
One, there's really
two different ways
to take a look at closures.
There's this expanded syntax.
There's also a condensed syntax.
Let me show you the
condensed syntax.
Swift has a very strong
type inference system.
We can infer a lot of things
based on context and the kind
of data types that
you are using.
So for example, if you have
a single line in a closure,
it's assumed that it's going to
return a value, so I don't have
to put the return in there.
And in fact, has prefix gives
a return type of Boolean
so Swift can infer
that the return type
for this closure is Boolean,
so I don't really need
to have this here, which
means I don't have to have
that there either, and since
atomic elements is an array
of atomic elements, I
don't have to specify
that an atomic element
is being passed in,
so I can get rid of that.
And since I don't have any
parameters, I don't have
to separate the parameters from
the code, so I don't need the
in keyword, which means I can
get rid of that and just tidy
up the spacing a bit, and I end
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
up the spacing a bit, and I end
up getting something
that looks like this.
And because the last argument to
the filter method is a closure
in itself, we can make it
into a trailing closure
and just get rid
of the parentheses,
so we get that looking
more like this.
Now the only trouble is
that there's no symbol
declared called atomic element
because I removed it.
But I'm passing in one item into
this filter closure every time,
and I can reference
that argument like that.
That's exactly the same code.
If you were missing Perl,
well, now we have this.
[ Applause ]
So now we can do a check.
Let's just put in a
letter N, and great.
Now I have the elements, but
they are not actually sorting
in the right direction.
Actually, they are
not sorting at all.
So, let's just quickly
go ahead and add a sort.
I am just going to
chain it to the end
of this existing closure.
Dot sort. Now, in this case,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Dot sort. Now, in this case,
I could double-click this blue
token, have it expand out,
and there's many ways we can
compare strings, like case
and sensitive compare, localized
compare, so forth and so on.
But because I know that in
a sort I am given one atomic
element, and then I'm given
the other atomic element,
and I only have to specify which
one comes before the other,
or if the first one comes before
the other, I can just write
out a closure myself, first of
all recognizing that I am going
to have two atomic
elements passed in.
So I have the first one, I
am going to check its name,
and I have the second one.
I am going to check its name.
That in itself is
not a comparison.
However, in Swift,
we've overloaded many
of the standard operators, like
greater than and less than,
so they actually work on types
that you might not expect them
to work on, like strings.
That's a string comparison.
I'll run it again.
Search on letter N, and now
it's being properly sorted.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Search on letter N, and now
it's being properly sorted.
It's filtered and sorted
all in just one line.
I can check again.
Let's search on S.
We have some really
important elements,
like Swiftonium [laughter].
Very important.
Okay. Let's come back.
So in this section, we
implemented filtering
of our table view by using
Swift's filter method.
And then we sorted
it using sort and got
to see an overloaded operator,
that being the less
than/greater than.
The next feature and final
feature that I want to add
to this app is simply one that
lets me select multiple rows
and then it adds them up to give
me the sum of the atomic weights
for the selected elements.
It looks kinds of like this.
We start off with
the table view.
The nav bar at the top is
saying select two or more items.
You select two or more
items, and then the nav bar
at the top specifies the
sum of their weights.
To do this, I start off
with my content array.
That's all the atomic
elements that I have,
those are all the ones that are
displayed in the table view.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
those are all the ones that are
displayed in the table view.
But what I want is just the
selected atomic elements.
So to get that, I can't
go to the table view
and ask the table view for
the array of selected items.
I can only ask it for the
index paths for selected items.
So the table view will give
me an array of the index paths
for the selected items.
I can query those index
paths to get the row.
I can correlate that with the
backing array, the content.
And from that, produce
the selected elements.
Basically, it's this.
We make a new array.
We loop through the index paths.
And then we go back
to content and we pull
out the corresponding
atomic element
for the current index
paths' row.
If you've been accustomed
to writing code like this,
you can do the same
thing in Swift with this.
It's the map function.
The important part is still
there, it's the part in orange.
Just all the the extra
infrastructure that's
around it has been removed.
Next, once I have an array
of all the selected items,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Next, once I have an array
of all the selected items,
I want to add up
their atomic weights.
So to do that, I might
traditionally use a foreign loop
where I first declare a
variable that's set to zero,
then I just iterate through the
selected objects and just add
to that, in this
case, D, their weight.
If you are accustomed to
doing this kind of thing
with a for-in loop, you can
do the same thing in Swift,
with a reduce, where we set
the initial value as zero,
and then we just
patch in a closure
that takes the initial value,
that's zero, and then appends
to it the next item being
passed in, adds them all up.
Let me just show you the code,
though I will give you a preview
that we can do the whole thing
with just a single
line of code like this.
One of the graphic
designers was asking me
when he saw the presentation,
can you make that
fit on one line?
It's like, not really, no,
I don't think you
could see it then.
Let's switch over to
the demo computer.
Alright. So here's the
function that does all the work.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Alright. So here's the
function that does all the work.
I start off by ensuring
that I have more
than two items selected.
So you can see I am using a
where clause on an if to say
that as long as the selected
items count as greater or equal
to two, continue
executing this code.
Then I use map so I can
actually get the objects
that are selected by the table
view, not the index paths.
And then use reduce
to add it all up.
And then in the end just passed
it through a number formatter
and stick it up in the title.
If you wanted to see the
same thing on a single line,
it would look like that.
When I run the program,
it adds them all
up and puts them up top.
That's showing map and reduce
to make it easier to work
with items that back
a table view.
Alright. In summary, I'm hoping
you will see that there's a lot
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Alright. In summary, I'm hoping
you will see that there's a lot
of advantages to
incorporating Swift,
even with your existing
Objective-C projects.
It's not hard to do, and
there's a lot of benefits to it.
You don't have to throw
away any existing code,
and you get to use these
modern and powerful techniques,
like reducing and maps and these
powerful structs, so forth.
For more information,
come see us in the labs,
check out our documentation,
check out the Developer Forums,
or send Stefan an email.
He loves to get mail.
You can just tell him the
conference is going well,
that would be fine.
And with that, I'd
like to thank you,
and have a great conference.
[ Applause ]