WWDC2014 Session 406

Transcript

X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
[ Silence ]
[ Applause ]
>> Good morning.
Welcome to Session 406:
Integrating Swift
with Objective-C.
Now many of the Swift demos
here at the conference start
by creating either a
brand-new project from one
of expo's project templates or
a playground and that's great.
Playgrounds are a great way
to explore the Swift language.
But many of you already have
Mac or iOS apps and some
of you have spent years
writing Objective-C code.
You have written it carefully.
You have debugged it,
polished it, optimized it.
It's just perfect, hmm?
And we certainly don't want
you to have to throw any
of that away or rewrite
anything that you don't want to.
Also some of you have really
a lot of Objective-C code
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
that you maybe spent a decade
writing and maybe it's...
not-so-perfect.
It's not as clean and shiny.
So you are going to be using
that Objective-C
code for a while.
You might want to rewrite
it, but it's not going
to happen this time around,
because you've got an app
to ship, etc. So,
Objective-C is not going away.
Both Swift and Objective-C
are first class citizens
for doing Cocoa and
Cocoa Touch development.
And, in fact, we don't
want you to spend any time
that you don't have to.
We don't want you to stop
refining the Objective-C code.
We want you to be able to
integrate Swift as you want to.
And we do think that as
you implement new features,
as you finally get around to
replacing crufty subsystems
that you want to
use Swift for that
because of its better
type safety,
its expressiveness,
its performance.
Now, many of you have also
gained a lot of knowledge
and experience in Cocoa and
Cocoa Touch through your years
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
and experience in Cocoa and
Cocoa Touch through your years
of Objective-C development and
that applies to Swift as well.
You're using the same
frameworks, the same classes,
the same APIs with some
syntactic differences, in fact,
the same design patterns,
such as delegation.
So all that knowledge applies
and so we expect that a lot
of you are going to end up with
projects that contain both Swift
and Objective-C and we
worked hard to make sure
that Xcode lets you freely
mix Swift and Objective-C.
So this session over the
next hour we're going
to be talking a little
bit more detail
than what you've probably
seen so far about exactly how
that works in practice.
We're going to start simple by
just introducing some Swift code
into an existing
Objective-C app.
Then we'll get a little
bit more interesting.
We're going to start
to add some Swift code
to a model-level framework,
which is then embedded
in an Objective-C app and
call that from Objective-C.
We'll talk about how to start
accessing your pure Swift
framework which will
start showing
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
framework which will
start showing
up from an Objective-C app.
And then we'll talk as we go
along about how to adopt some
of the common, well-established
Cocoa patterns in Swift.
And again Swift is
designed from the ground
up to be a great Cocoa/Cocoa
Touch development language.
And so it has language support
for some of the Cocoa idioms
that we've gotten used to.
So, I could go on here for, you
know, two dozen slides and talk
in detail about how this works,
but I'd rather just give a demo
because that's usually
a bit more clear.
Alright, so I've
got a project here.
It's, it's an iOS app and
it has an embedded framework
so it's decently designed here.
It's got a top layer which is
the actual app with the view
and the controller
layer in our MVC design.
There is a framework
called ListerKit down here,
which contains our
model objects.
And all of this is currently
written in Objective-C.
Just to give a sense of
what this is, let's go ahead
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Just to give a sense of
what this is, let's go ahead
and run it in the simulator
and we're gonna go ahead here
and bring up the simulator.
We see that, yeah, it's a
typical list-taking application.
I can go in and add
colored lists here.
I can check off items,
etcetera, edit them.
What I can't do right
now is to add a new list.
This happens to be an iCloud app
so I could do that in my Mac app
and it would show up here...
But, for this example
we're going to use Swift
to add the ability to
add a new list in iOS.
Alright, so I'm going
to go back here
and the first thing I'm
going to do is look down here
to the group ViewControllers.
And here we have some
ViewControllers already written
in objectives [ahem]
in Objective-C.
I go to File, Create New File,
and in this case I'm just going
to create an empty Swift file
because I want to show you how
to get started from scratch.
Xcode has great file
templates and can fill
in a lot of code for you.
And we want to do it
in a basic way here,
to show how it's going to work.
I'm going to call it
NewListController and I'm going
to make sure that
it is going to go
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
to make sure that
it is going to go
into the application
target here.
We also see that I have
ListerKit, which is my framework
and one of the new
extensions, here,
for showing in the status view.
So, NewListController.
Now as I create this,
my first Swift file,
in a pure Objective-C project...
Xcode will offer to create
for me what's called
a bridging header.
So a bridging header, we'll talk
about that in more detail later,
but basically what it is, is
a way for you to expose some
of your Objective-C
APIs to Swift.
I'm going to go ahead
and create that.
So I get that by default.
Now, import foundation...
actually I'm going to be
needing some UIKit API here.
So I go ahead and
change that to UIKit.
And I know that I will need to
import my own model framework
because we want to obviously
create our model objects
from within the ViewController.
And I'm just going to go ahead
here and say NewListController.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
And I'm just going to go ahead
here and say NewListController.
And now, when I complete,
we see that because I said
"import UIKit" there,
Swift pulls in the
Objective-C framework UIKit
and gives me a Swift
view of those symbols.
So I'm going to subclass
from UIController
and just leave it
like that for now.
So we have just a
basic empty class --
a Swift class, subclassing
an Objective-C class --
that we get from UI-
...from Cocoa Touch.
Now we could sit here
and drag out rectangles
and create an API-
...create a UI for this,
but fortunately I happen
to already have one.
How lucky.
It isn't wire up though,
so we're going to
do that right now.
The first thing I'm
going to do here is,
just like with Objective-C,
I need to set what the class
of my UIController is.
So I can go up here and now we
see that the NewListController
that I just created is shown
here among all the ones
implemented in Objective-C.
So when you access your classes
from Swift-...from
Interface Builder, the Swift
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
from Swift-...from
Interface Builder, the Swift
and Objective-C classes
show up next to each other
and you can work
with them as equals.
Alright, so now the next thing
I want to do after setting that,
I'm going to go ahead and
create a bar button item here.
I'm going to drag that up
into the toolbar, up here,
and I'm going to change
the identifier to be Add,
and that also changes
the visual appearance
to give it a standard Cocoa
Touch "Add button" look.
I'll create a segue, it's
going to be a mobile segue.
This is, after all,
the storyboard
and I'll change the name of
that segue to be New List.
Now that's going to be important
a little bit later on so
that we can access it
from within our code.
Okay. So now, you
may have seen in one
of the other demos how we can
drag from Swift code and connect
to objects inside of IB.
We can do the same thing in
the other direction as well.
If I option-click here
on my Swift class --
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
my ViewController class
implemented in Swift --
now I can drag the
other direction.
First thing I'm going to do is
I'm going to go ahead and drag
from an outlet and
insert a new outlet here.
And I'm going to call
this "nameField".
And we see that it's a UIText
field and the storage is weak
because we have a
controller class,
the super view- ...the view
hierarchy owns the button,
and the fields and
the buttons in here,
and so we just want a
weak reference to it.
And, in fact, when
we insert this,
Interface Builder knows
it's a Swift file.
So it uses the Swift syntax just
as it would for Objective-C.
And it uses the IBOutlet
property
for the stored property- ...I'm
sorry, the IBOutlet attribute
for the stored property here.
And this is implicitly
weak and also optional
because not all the
outlets may be connected.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Give myself some space
there; I'm going to go ahead
and do the same thing
here for the buttons.
I'm just going to control-drag
these and I'm just going
to fill these in
in the same way.
Now this is a sample app.
Let me go ahead and
adjust the spacing there.
This is a sample app and
so it's not rescalable
to just have six
buttons and six outlets.
But I know that you
all design better,
more maintainable
code than this.
This is for illustrative
purposes only.
You might want something
where you can load colors
from a TableView- ...uh,
from a Plist and show them
in a TableView or
something like that.
I'm going to go ahead and
finish connecting these.
Alright, so now we have our
outlets to the UI components.
We can also create
Actions in this way.
If I choose the Cancel button
down here and I drag in the,
I can create an Action up
at the top here just for-
...just as for Objective-C.
I change the connection
type to Action.
I will type here "cancel" and
we can just leave the type
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
I will type here "cancel" and
we can just leave the type
of the sender as
any object here.
We don't actually care
because we're not going
to be messaging it.
What we will do, however, is
to invoke some Cocoa Touch API.
I give myself some more room
and I can say "self.dismiss"
and then the code
completion gives me access
to the Cocoa Touch API
that we already know.
I can complete here.
I'm not going to
need a- ...I want it
to be animated, so I put "true".
I don't need a completion
block, so I give it "nil".
Now, in Objective-C we
always have to message "self"
if we want to talk to self.
Swift knows from context when
you are messaging yourself.
So we can actually
leave this out
and make this a little
bit more readable.
[ Applause ]
Excellent.
Alright, so now we will connect
the buttons here as well.
And I'm going to be
calling this "pickColor".
Now in this case
I choose UIButton.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Now in this case
I choose UIButton.
(I'm sorry, I need to
make it an Action.)
I choose UIButton down here
because in this case we
will actually want to talk
to the button, or
compare the button,
so we want to have that type.
And as you can see, Interface
Builder generates the code
and makes the sender
be a UIButton.
Before I forget, I'm
also going to wire
up the other actions
here as well.
Let me see here.
To connect to this one,
give myself a little
bit of space there.
And now they're all wired
up to the same Action.
So they're all wired
up to the same Action.
The sender will be
different depending
on which button we touch.
Now, I want to show
that some of the power
of the Swift select statement
here, the switch statement.
Unlike Objective-C or
other C-based languages,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Unlike Objective-C or
other C-based languages,
I can actually switch
on a wide variety
of different kinds of patterns.
In this case I'm just
switching on what the sender is.
But this is a very rich
construct in the Swift language
and I encourage you to
catch one of the later talks
about advanced Swift to
really get into the depth
of what you can use this for.
For right now, to keep
it simple, I'm just going
to use it to match on a button.
And what I will say is "okay,
now I need to assign
something here."
So I'm going to need to
have a stored property.
I create a stored property
called selectedLabel
because we're actually
choosing labels here represented
as colors on the screen.
And then what I can do
here is I can say this is
an AAPLListLabel.
So this again is an Objective-C
type that has been imported
through my import of
ListerKit up there.
I'm going to start out by saying
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
I'm going to start out by saying
that that is an
APLListLabel.Gray.
That's the default value of it.
Because I don't put a question
mark, I don't make it optional,
that means that it
has to have a value.
Remember the IB outlets
are implicitly optional.
Now it's kind of ugly to have
to repeat the same thing here.
And, in fact, Swift has great
support for type inference.
And when it knows exactly
what type you mean,
then you can go ahead
and leave out the type.
So from this, Swift can tell
exactly that, "okay this has
to be an AAPLListLabel," because
that's how I'm initializing it,
so I can just leave
out the type.
Now I can complete
my statement here.
I can say "selectedLabel
= .Gray" and, in fact,
I can add to the rest
of them here as well.
I'm a very fast typer.
Now we see here that
Objective-C- ...I mean, sorry,
Xcode, Swift has a
message for us here.
So in Swift, one of the
things to notice is each
of the cases is implicitly ended
at the beginning
of the next case.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
at the beginning
of the next case.
So you don't have the case of
C where you implicitly fall
through and that's the
source of a lot of bugs.
In fact, Swift was explicitly
designed to avoid many
of the common pitfalls that are
common in C-based languages.
Now in this case, another thing
about the switch statement is
that it has to be exhaustive.
So you have to say that
you cover all the cases.
In this particular case I'm
just going to add a default
and what I'm going to do
in this case right here is
to use a Swift standard
library function
to just trap into the debugger.
Because I want to make sure
that if I just add new buttons
and hook them up that I actually
have the code for that as well.
So now Xcode is happy.
That's excellent.
We've just set our property.
That's all we've done so far.
But now we can use some
more of the Cocoa Touch API
to actually make the button
show a reflected state-
...show it's selected
state, I should say.
So, we say, in this case
we can say "sender."
and then we can use our code
completion to access the layer.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
and then we can use our code
completion to access the layer.
And then we'll set it's
borderWidth to be five.
And then, of course,
we have to initialize
that to be a borderColor
as well.
Now in this case, because
we're accessing the layer --
that's a core graphics API --
we get the CGColor from the
UILabel- ...the UIColor.
And all of these are
standard Cocoa Touch APIs.
So you see that when we access
our own Objective-C APIs
or the Cocoa Touch APIs
it looks all the same.
We still use the same,
nice Swift syntax.
Another thing we can
do here is to just say
that the view's tint color
is going to be the color
that corresponds to the label.
And for this we can call
another one of our functions
that we brought in
from Objective-C.
And we pass it the
selectedLabel.
And, in fact, if we
command-click on this,
we see here that we can get
to a Swift representation
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
we see here that we can get
to a Swift representation
of our own Objective-C API.
In Navigate we could actually
go back to the header itself
that we declared it from.
Alright, so now those of you
who are following closely here
may have seen a little bit
of a problem: we set
the borderWidth to 5;
we never actually deselect
the previous button.
So what we can do
is to keep track
of the currently
selectedButton and in this case,
I'm going to explicitly say that
it's a weak stored property.
So this is not going to cause,
ah, the- ...extend the lifetime
of that button which
is, after all,
owned by the view hierarchy.
And I'm going to declare
that this is a UIButton
but it's optional.
Because when we first
start out it has no value.
And so, we're going to be able
to check whether it
has a value or not.
We add some code to set the
selectedButton to the sender
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
We add some code to set the
selectedButton to the sender
at the end of having
assigned it.
And then here we can say "if
selectedButton" then we're going
to go ahead and clear out
the border on that one.
Now this happens to be a object
reference so I could do the same
in Objective-C with a nil value.
But one of the nice things
about optionals in Swift is
that they apply to any type
of value even a Boolean
or a scalar.
So in this particular case,
I'm just going to go ahead
and select the selectedButton's
layer borderWidth to 0.
Now you'll notice the
exclamation mark there.
This is the unwrapping
operator and that says
that if I have an
optional value,
then when I've determined
it has a value,
I get at the actual value.
And if I forgot to check
whether it has a value,
that will cause a
trap if it doesn't.
So this is one of the ways
that Swift makes it safer
to write your apps, because you
can catch these errors earlier
without having to add a lot
of assertions to your code.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
without having to add a lot
of assertions to your code.
So I think we're
probably good to go here.
Let's go ahead and take a look
and I'm sure I forgot
something somewhere...
"selectED button"!
And that's because
I mistyped it.
So that shows that
this is live right?
I'm not playing a
QuickTime movie up here.
[Laughter] Okay,
so now we're here.
We have our add button.
We bring up the modal sheet.
As we select the various
buttons we can see
that we have the border and
then we do the, the tint,
we tint the buttons down
here and, of course,
the Save button doesn't
yet work.
Now I'm just a UI guy.
So I just make it look pretty.
But to actually do the model
level work we're going to have
to ask one of my colleagues
to come up here in a moment.
First let's take a look at
what we did in this part.
Okay. So what did we just do?
So first of all, we added some
Swift code to an Objective-C app
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
So first of all, we added some
Swift code to an Objective-C app
and Xcode offered to
create us a bridging header,
which the next action we'll go
onto some more detail about.
We subclassed the Cocoa class-
...Cocoa Touch class in Swift.
In this case it happened to be
a Cocoa Touch class that's part
of the standard libraries
of Cocoa Touch itself.
But it could also be one
of your Objective-C classes
that you've declared.
We used Swift class in
the Interface Builder
and as I hope you saw,
it's exactly like working
with an Objective-C class.
And that's actually a theme
under here that we wanted
to feel like exactly the same
thing; it's interchangable.
And so now to make this a bit
more real in terms of the model,
I'm going to ask my
colleague Dave to come up
and complete the project
here or work on it further.
[ Applause ]
>> Alright.
Thanks Anders.
What we saw is that you
have access to everything
in Cocoa Touch and the power
that you've got in Xcode
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
in Cocoa Touch and the power
that you've got in Xcode
from Swift without
really doing anything.
But I'm sure what
you're thinking now is
"what about MY code?
What about the Objective-C
code that I have
in MY target in MY app?"
And that's what I
want to show you.
As Anders said, we could
spend a lot of time in slides
and everything else
and I'm actually going
to cut it a little
shorter than he did
and just go straight
into a demo.
And so we come back right
where we were before
and for our purposes the first
thing we want do here is a
little bit of tidy-up,
and that is
to make our UI text
field behave the way
that we want for our user.
It offers a great deal
of configurability using
the delegate pattern.
And to do that we're going to
make NewListController conform
to UITextFieldDelegate,
which is as easy as...
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
if I could type...
finding our UITextField...
Man, I'm having some time here.
[laughter] It's a
comedy show, I promise.
[laughter]
There we go.
Alright, something to
call out there is-...
[ Applause ]
Success! And that's the
hardest thing I'm going
to do the whole time
I'm up here.
[laughter] So
UITextFieldDelegate,
all we did here was add the
protocol conformance to the end
of a common separated
list after our class.
In our case it's important
because we're actually,
we actually have a superclass.
The superclass is the
first thing in that list
and then our protocols follow.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Now that I have managed
to declare textual delegate I'm
actually going to take it easy
for the next part
and just bring in...
the implementation of
TextField should return.
And the only thing we're
doing here is resigning first
responder on our
TextField and returning
to make sure the
keyboard dismisses
when the user hits return.
Not particularly interesting,
but it was just as easy
as it would be in
Objective-C to go ahead
and implement this protocol.
Next, as Anders promised, we are
going to look a little bit more
at the interaction with
the model side of things
and actually implement Save.
The first thing I'm
going to do in-
...who caught the error here?
Somebody knows what I
forgot to do, and that is:
manage to declare it as
a UITextFieldDelegate
but we never actually connected
it and that would have made
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
but we never actually connected
it and that would have made
for an interesting end.
So now that we've got that set
up I will control-drag over,
manage to hit the button, set it
up as an action, call it "Save".
Now, inside here I
actually want to interact
with the representation of
our documents that we see
in the document list
on the main screen.
And that's a part of our app
target, not a part of ListerKit.
And because of that we have
to take a different approach.
We can't just simply
import our framework in.
And this is where we make use of
the Objective-C bridging header
that Xcode offered
to create for us
when we created the first
Swift file in this project.
So we'll navigate over to
that and it gets created
in the same directory -- or in
the same group, I should say --
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
in the same directory -- or in
the same group, I should say --
as the Swift file
that we created.
It's not overly interesting
at first.
It simply has a comment
that's very instructive.
This is where we put the imports
for the classes in our target
that we want to expose to Swift.
In our case, it's a single class
and that is our ListInfo class.
This is the representation that
backs the TableView that you see
when the app initially comes up.
We save that and head back to...
really don't need the storyboard
anymore, so, I'm gonna go ahead
and collapse things down and let
us actually focus on the code,
because the wrapping really
kind of gets on my nerves.
That's one of the things
that I like about Swift,
is it's a little more succinct.
So just heading back
down to our Save...
Now that we've pulled in
the bridging header - or,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Now that we've pulled in
the bridging header - or,
now that we've added our class
to the bridging header --
we can actually create one.
I'm going to create
it as a "let"
because it's going
to be constant.
I'm only going to
assign it once.
And so my ListInfo
is equal ListInfo...
...and, of course, I
keep saying "ListInfo"
like we instruct you guys to do
when we're writing Objective-C
code we actually use a prefix.
And I've been writing a
lot of Swift code lately,
so it's not on the
forefront of my mind.
So, our ListInfo...
and it offers us
two constructors.
One of them takes URL.
The other takes an
NSMetadataItem
for that iCloud support
we were talking about.
We're going to go
with the URL one.
I could construct
the URL right here.
A little bit of forethought,
thinking forward,
I know that I'm actually
going to want
to revise this a little bit
later and iterate on it.
So I'm going to set
up a computed property
as a convenience.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
up a computed property
as a convenience.
Now to declare a computed
property is pretty similar
to the way that we
define a stored property.
And we'll go with var.
I'm going to call it "fileURL".
Now with a computed
property we do have
to be explicit about our type.
I'm going to make it
optional and that's
because if the user actually
hasn't provided us a value
in the TextField yet, I can't
actually construct a valid URL.
I've got an implementation ready
that just happens to make use
of the list coordinator
from down in the ListerKit.
And after defining the computed
property I can come down
and make use of it right away.
And you'll notice as Anders
had mentioned earlier we don't
actually have to
explicitly refer to self
when accessing our property.
We set up a few pieces of
information that we've captured
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
We set up a few pieces of
information that we've captured
for the user so we'll get the
name from our nameFields.text
and the label is
going to be equal
to our selectedLabel
and that looks good.
So now that we've set it up, we
actually want to save this out
and ListInfo provides us
a convenience for that.
And we will createAndSaveWith
CompletionHandler.
And this CompletionHandler
is actually going to call-
...is going to come back to us
with a Bool letting
us know whether
or not the save was successful.
And I could use what code
completion's suggesting here
and fill
out my completionHandler closure
inside of the parentheses.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
out my completionHandler closure
inside of the parentheses.
But because it's the
last argument in the call
and because it's a closure and
it's going to be a little long,
I'm actually going to make use
of Swift's trailing
closure syntax.
And in this case I don't
even have to put the parens
after the method call
because our createAndSaveWith
CompletionHandler
actually doesn't take any
other arguments.
So inside of here I will note
the variable name that's being
passed in, which is success.
...having more typing problems
so that you guys can
get a good laugh.
And I'll just check the value
and for now I'm just going
to print a little debug message
to let me know that
it was saved.
And just like Cancel, we want to
get out of the user's way now,
so we'll go ahead and dismiss.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
So let's build and run, see
that everything is doing
what we want it to do,
make sure that everything
is good to go.
Bring it up...
Let's see, I need to go
to the hardware store
a little bit later.
And so we'll name our
list hardware, hit return.
Keyboard goes away;
that's a good sign.
The hardware store I
frequent actually uses orange
in the logo rather
heavily, so that's going
to help me remember
what's going on there.
And I'll go ahead and
hit the Save button.
That's not really
what we wanted.
I really wanted to see my
hardware list show up here.
Did it actually save?
I'll go over and check
the debug console.
Indeed it did save and just
for fun I'll actually stop
and run the app again.
And you'll see that it actually
did save and get created.
But we never passed anything
back to our document list
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
But we never passed anything
back to our document list
to let it know that we created
anything new and we kind
of wanted this to
not be sitting there
and polling the file system
over and over and over again,
just looking for new
files that might show up.
So for our purposes,
we're going to take a page
out of UITextField's book
and we're actually going
to use Delegation to
pass information back
to the ViewController
that presented us.
The first step there is
usually to define a protocol
that we expect our
delegate to conform to.
And in Swift we can do that just
as easily as declaring a class
or a struct or anything else.
We'll just start with the
"protocol" keyword (that I
actually spelled correctly)
and NewListControllerDelegate.
And I already have a method that
I want my delegate to conform
to that I've prepared that
just takes a new ListController
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
to that I've prepared that
just takes a new ListController
and a ListInfo letting it
know what's been created
and done over here.
Now at this point this protocol
is visible to my Swift code
but I want to make sure that
my Objective-C code can see it
as well.
To do that it's as simple as
adding an attribute at Obj-C
and this is going
to go ahead and say,
"even though this
is all Swift here,
we do want to make
this available
to Objective-C a
little bit later on."
So now that I have a delegate
protocol I'll actually define a
delegate and we'll make it weak
because we don't
really want to own it.
We don't want to
extend its lifetime.
And I can declare
it as being just
of the NewListControllerDelegate
type.
I don't have to do something
like ID and angle brackets
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
I don't have to do something
like ID and angle brackets
around my protocol to
indicate what's going on here.
I can simply have a property
that is of this delegate type.
It's going to be optional
because I may not
have a delegate.
So now that we've defined a
delegate property I think maybe
we ought to do something
a little more useful
than printing a debug
statement down here.
And for that we're going to
reach out to our delegate
and I'm going to make use
of another Swift feature,
which is optional chaining.
So the delegate is an optional.
It may or may not have a value.
By adding a question mark
here before actually calling
out to the method that we want
to invoke, I'm going to be able
to say, "IF I have a delegate --
if there is a value
there -- make this call.
If there's not, don't
do anything."
[ Applause ]
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
[ Applause ]
And now I'm trying to remember
what the name of it was.
Ah, there we go.
Pass my self and the
ListInfo that we just created.
(REALLY not much of a typist,
in case you can't tell.)
And at this point we've wrapped
up the Swift implementation
of this, but we've
defined this delegate.
Now we actually need our
Objective-C code to be able
to see the Swift code
we've just put together
and interact with it.
To do that I'm going to head
back over to the document-
...the, the controller
for the document list,
and inside of here we're
brought to- ...I mean,
what comes to your
mind is the fact
that the way I make one object
in Objective-C available
and visible to another object
is by importing its header.
Well, with Swift we
don't have headers.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Well, with Swift we
don't have headers.
We think that's a good thing
within Swift, but we've got
to find a way to bridge the
gap back to Objective-C.
And so in that case Xcode's
got our back and is going
to provide us with
a generated header
that contains the entirety
of our Swift interface.
Being generated,
obviously it has
to have a well-constructed
naming scheme, and that's going
to be our product name --
which in this case
is "Lister-Swift.h".
And so this is a generated
header, so I'm going to go ahead
and take the time to
build, to make sure
that we've got the most
up-to-date items in it.
And after importing that we will
come over here and we're going
to go through basically
the same process
that you would ordinarily to
implement any of the Cocoa
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
that you would ordinarily to
implement any of the Cocoa
or Cocoa Touch delegates
on your own class.
We'll add our protocol
conformance and we can pull
in our
NewListControllerDelegate.
I'm going to choose to
use the jump bar to head
down to an appropriate
place I feel
to go ahead and implement this.
And as I code complete here
with the NewListController
didCreateListInfo, you'll notice
that even though we declared
this completely in within Swift,
what we get is a syntax
that matches what I'd expect
from Objective-C so that
everything still feels natural;
everything feels like a first
class citizen in its place.
Happen to have the
implementation...
Wellllll...
The implementation of
the last thing ready.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
The implementation of
the last thing ready.
In this case what we're really
doing is just updating our data
source and then refreshing
the TableView
with a little bit
of new information.
And the very last thing
here, or the final step,
is that we actually
need this ViewController
to make itself the delegate
for our new ListController.
We're using a modal segue,
so why not do it
in prepareForSegue?
Down here we'll just
check to make sure
that it's the right segue.
We'll grab the newListController
and we'll set ourselves --
in this case our
ListDocumentsViewController --
as the delegate.
At this point I fully expect
things to do what I want.
And... Coming up with another
list, "WWDC Must Haves".
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
And... Coming up with another
list, "WWDC Must Haves".
Not really sure what
those are yet,
but I'm pretty sure I'm
going to think something up.
And this time it
actually comes through,
shows up in our
DocumentViewController
and just for, you know,
proof's sake I can come into it;
I can change it around.
I really like this salmon
color that they're using
for the Apple badges this year.
So I'm going to actually
pick that
and that's integrating our code.
[ Applause ]
And just to head back to
the slides for a second,
what we've just seen here
is that it's very easy
to expose our Objective-C
code to Swift.
Simply import whatever
headers you want
into the bridging header and
they'll be made available
to the Swift in your target.
We defined a protocol in
Swift which was as easy
as defining a protocol
in Objective-C.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
as defining a protocol
in Objective-C.
And finally, we've exposed
that protocol and the class
that we just created
in Swift back
to the Objective-C
code in our target.
And we actually didn't
have to do anything there.
Xcode did the generation
of the header
that provided that interface.
So just to reiterate a little
bit more when we want to go
from our Objective-C headers
and make our Objective-C objects
available to Swift we're going
through the bridging header.
This is going to be
created- ...Xcode will offer
to create this for
you automatically
when you add a Swift file to
an existing Objective-C target
or an Objective-C file to
an existing Swift target.
When we want to get our
Swift interface exposed
to our Objective-C
implementation,
there we're looking at
the generated header
that Xcode provides and
this is going to be named
after our product
and it's going to be
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
after our product
and it's going to be
"our product name
dash Swift dot h".
One last thing to
remember about these,
is that the bridging header
is created for you by Xcode
but you own it from
that point forward.
So it should be something
you check
into your source control system.
It should be something that
you maintain, you manage
and it's there so
that you only expose
to Swift what you want
to expose to Swift.
In the case of the generated
header, it's just that.
It's generated as part
of the build process.
It's going to contain your
com- ...the complete interface
for the Swift files
within your target.
And those are going to
be readily available
to Objective-C simply by
importing that header.
So at this point to talk
with you a little bit more
about what you can do with
frameworks in your product-
...in your projects, I'd like to
invite my colleague Jordan up.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
...in your projects, I'd like to
invite my colleague Jordan up.
[ Applause ]
>> Thanks Dave.
Alright, so we've seen
how easy it is to deal
with integrating your Swift code
into an existing
Objective-C app.
And Xcode pretty much takes
care of all the details for you.
It's available in
Interface Builder.
It's going to show
up in the simulator.
Everything works just fine.
So I'd like to move a
step down in the hierarchy
and look at the framework.
So at this point we've been
working pretty much the entire
time up in this Lister
part of the application,
which is the actual
application target.
Now we're going to
move down to ListerKit
which handles the model and the
object graph for this target.
And specifically we're going
to add a new functionality:
the ability to add
attachments to every list item,
something like, oh, a
photo or a voice recording.
To do this in Objective-C
you would create a new class,
add it to your object graph
by adding a new property
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
add it to your object graph
by adding a new property
and then setting up an interface
to mange it and we're going
to do the exact same
thing but using Swift.
So I'll start here by
going to File, New File
and this time let's use the
Cocoa Touch class template.
I can name this
APLListItemAttachment.
Subclass of NSObject and the
language, of course, is Swift.
I make sure that it's
in the ListerKit
target and click Create.
You notice here that
Xcode did not offer
to create a bridging
header for us.
This is because we're working
with a framework target.
In a framework you
already have a header
that specifies the entire
contents of the framework.
That's your umbrella header.
So when you're working
with a framework target
in Swift everything
that's available
in your umbrella header will
automatically be visible
to the Swift code,
no work required.
So we're going to want
attachment objects to persist.
So the very first thing I'm
going to do is add a conformance
to a standard Cocoa
Touch protocol, NSCoding.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
to a standard Cocoa
Touch protocol, NSCoding.
Attachments are made
up of two things:
their data (which will I
represent using NSData)
and their type.
For types of data
Cocoa Touch likes
to use uniform type identifiers,
which are a very general way
to represent file and data types
that the user just
stored as Strings.
The next thing to do here
is to declare an initializer
and that just takes the
data and type from outside
and will assign them
into our own fields.
But we do need to be
careful because the data
that we are given might
be an NS mutable data
and somebody might
try to change it
after they've created
our attachment.
So in order to work around this,
we need to call the copy method.
However, copy returns
ID in Objective-C
and in Swift that's
going to come
through as the Any object type.
We can't go directly from any
object to NSData in Swift,
because that's not
guaranteed to be safe.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
So instead, we have
to explicitly tell the
compiler to do a cast here.
And this is important, because
if anybody ever implements the
copy method incorrectly
and doesn't return an NSData
we would much rather find
out about it right here in the
initializer than crash somewhere
down the line when
we try to use it.
On the other hand, we can
just assign the type directly
and this is because
strings in Swift are values.
Unlike NSString, every
Swift string has an
independent existence.
And when you assign across
using the equal operator you get
independent strings.
So, whatever somebody
does over here
to their string won't affect
your string over here.
[ Applause ]
So next I need to implement
the NSCoding protocol.
(Let's close this
for a bit more room.)
And here we can co- (ah!)
...code complete here, no
code completion, awesome.
So here I'm implementing
the NSCoder initializer
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
So here I'm implementing
the NSCoder initializer
and you'll notice something
a little funny here.
In Objective-C we'd have the
method named initWithCoder.
But in Swift we have an
initializer that's just
init coder.
So in order for more
consistency in the Swift world,
where object creation syntax
is a little different,
an init method from
Objective-C that starts with
"initWith" will come into
Swift with the "With" dropped.
This ensures a consistent
world with Objective-C.
Sure, the implementation of
this is pretty much the same
as what you'd expect.
Just call decodeObjectForKey
and again explicitly
downcasting to the type we want.
If somebody hands us a
bad archive we'll find
out about it now rather
than crashing later on.
Finally, the last thing
to do here is implement
the encodeWithCoder method
and this one does not
get any name changes
because that is limited
specifically to initializers.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
because that is limited
specifically to initializers.
And the implementation of
this, again, is very simple.
We call encodeObjectForKey with
both the data and the type.
And that's it; this is an
entire Swift model object
and it fits on one slide.
I didn't do anything crazy
or confusing here beyond some
of the things you need to do to
be a good Cocoa Touch citizen.
[ Applause ]
The next step, of course, is to
add this into our object graph.
So I go back to our
Objective-C header
and now we have a
bit of a problem.
Because last time that we
wanted to access Swift code
from Objective-C what we did
is import the generated header
into the Objective-C file.
But we can't do that here
because the generated
header depends on the Swift.
And the Swift reads the
entire public contents
of our framework (via
the umbrella header)
and that includes this
header right here.
So instead, I use
exactly the same mechanism
that I have in Objective-C.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
This is what we use
to break cycles.
It's a forward declaration
using @class
and I can forward-declare
the Swift class just
like I would any
Objective-C class.
And now I am perfectly able
to create a property here
using the attachment.
The last thing to do, of course,
is to update the implementation
of the list item to actually
handle the attachment and for
that I'm going to import
the generated header now
that we're in a .m file.
And because this is a framework,
and the generated header is part
of our public interface,
we are going
to use framework
syntax to import it.
All that's left to do here now
is to update the implementation
of this class so that
we actually handle this
new property.
So that means having an
extra key for encoding here.
Setting the attachment
explicitly to nil,
making sure that we can
encode and decode it.
And while I'm doing this
I'd like you all to note
that this is exactly
what you'd be doing
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
that this is exactly
what you'd be doing
if we had implemented the
attachment class in Objective-C.
There's nothing surprising
going on here.
It's exactly what you
would have done before.
Finally we do need to make sure
that we update the copy method
so that when we copy
a list item,
that we actually do get a copy
of the attachment as well.
(Square bracket.)
And since we made our
attachments immutable using that
"let" keyword to
define the properties,
we can just assign
this across directly.
We don't need to do any
special copying to ensure
that they are independent and
not going to change on us.
So that's it; that's
the entire model.
And now you're probably
thinking, "Oh great,
here we go again, back
to Interface Builder
to define a new layout, a new
ViewController and add a bunch
of outlets and actions and hook
everything up all over again.
We already saw that!"
And you'd be right...
except that, we're
actually really lucky here.
The three of us work in
a department with lots
of other great people and
another team is working
on a very similar app
that needs to deal
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
on a very similar app
that needs to deal
with these kind of attachments.
And because their app is new,
they've written it
entirely in Swift.
But they've also factored out
their attachment ViewController
into a separate framework,
which is a new feature of iOS 8.
So we're going to use their
framework, written entirely
in Swift, to implement
the interface that deals
with this model object.
To start off here I'm going
to go to the bottom here
and check the plus,
yet another way
to add new files to our target.
Choose "Add Files Here" and I'm
going to the media view folder
to select their project,
mediaviewer.xcodeproj.
Once I have this in my project
then I can just add this
to our target by going to our
own project, selecting the list
or target and in the embedded
binary section clicking
the plus.
This lets us add the
framework directly.
Notice what we just did there.
This is a new feature of iOS 8
to allow embedded frameworks.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Of course, if you're a Mac
programmer you're probably used
to this by now.
These embedded frameworks
need to be made available
to Interface Builder, to
other classes in your project,
linked into your binary so
that you can actually run it,
and copied into the app so that
when other people
download it they don't need
to download the framework
separately.
And Xcode 6 did all of
that for us without asking
if the framework was written
in Objective-C or Swift
or a mixture of the both.
[ Applause ]
So in our storyboard now.
We can zoom out, and...
let's bring up the
library here to bring
out a new ViewController.
And in this case I'm
going to set the class
of that ViewController to
be the media ViewController
from the other framework.
And you'll see the module
field here populates as well;
this is that feature of Swift
that keeps different
framework classes from stepping
on each other at runtime,
even without a prefix.
We don't need the view that
Interface Builder provides
for us, because it already
has one in its own framework.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
for us, because it already
has one in its own framework.
So I can just delete that.
And now, to make sure that
we can actually get to this,
I'll select the tables out here
and drag over- ...control-drag,
to create a new segue.
Make that a push segue and we'll
give that a name: "attachment".
Now we've set up
Interface Builder.
We've set up the interface
and the model object.
It's time to put them together.
And once again we're going
to use a delegate for that.
So let's go to our
Objective-C code.
This is the code that
displays the lists.
At the top here we have
the NotificationCenter
which is a system framework
and the import for ListerKit,
which is our own model
framework within this project.
I can add a third
import for MediaViewer,
which is the framework
in the other project.
I'm going to hit Build
again, to make sure
that everything is
compiled and up to date.
And now I get code completion
for the MediaViewerDelegate
protocol.
Just like before I'm going
to jump down to a nice,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Just like before I'm going
to jump down to a nice,
convenient place in the
application to implement this.
And the first thing I'm going
to do is implement a
little helper method here
that just returns
the selectedItem.
Again, even though this is
sort of details of, like,
how the application currently
works, I want you to remember
that everything we're doing
here is exactly what you do
in Objective-C.
From the perspective of this
code you can't really tell
that the entire framework
was written in Swift.
We even get code completion
for the delegate method here.
MediaViewController
didChooseNewMedia, type.
And now that we have all of
this set up it's really easy
to add an attachment to the
currently selected item.
So here we say that
its attachment is going
to be a new attachment
that we create now
by calling alloc initWithData.
Wait a second!
We designed this
attachment class in Swift,
and it had an initializer
named "initData, type".
But in Objective-C it's showing
up as "initWITHData, type".
So you see that this name
translation works both ways
so that you have
a consistent world
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
so that you have
a consistent world
in Swift and in Objective-C.
[ Applause ]
I'll do a bit of
housekeeping here to make sure
that we update our backing store
and now we have everything
set up here.
All that's left to do, just
like Dave did, is set ourselves
as the delegate for the
MediaViewController.
So this starts off the same,
where we set the attachment
and we set ourselves
as the delegate.
And then we can also set the
title of the NewViewController
and make sure that it's showing
the attachment that's actually
on the selected item.
Or if the selected item
does not have an attachment,
make sure that the controller
isn't showing anything.
So with that, everything
should be working.
Let's build and run.
Let's go into the
tech toys section here
and you see now we have
these disclosure indicators
on each of the items.
We'll go into Thunderbolt
this way.
Then I can see it
says No Attachment.
But I can click this Edit
button using the ViewController
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
But I can click this Edit
button using the ViewController
from the media views framework.
That brings up a
standard iOS control.
Making sure that our privacy
is protected, and, ah...
This kid looks like he'd really
enjoy a Thunderbolt display.
[Laughter] Okay, we can go back
to another one of these here;
managed to get myself
stuck editing.
There we go.
And you can see that the
image actually does persist
in the Thunderbolt
display section.
So it looks like
everything's working.
So you saw how easy it
was, to not just deal
with a new model object- ...or,
sorry, not just a new object
in your application target,
but also a new model
object in your framework.
Additionally, this is not
related to Swift at all.
But in iOS 8 we now have
embedded frameworks.
And Xcode 6 will handle all
the details of that for you.
Yes, clap!
[ Applause ]
Finally, once again, we
updated our interface
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Finally, once again, we
updated our interface
and added a delegate-
...adopted a delegate protocol.
And once again I want
to impress on you
that it's exactly what we would
have done had the delegate
protocol and the ViewController
been implemented in Objective-C.
We didn't have to do
anything unusual or different
because it was implemented
in Swift.
So you see how easy it is with
Xcode 6 to use user frameworks.
And in general we want you
to be treating those the same
as we do system frameworks.
So that means in Swift you'll
be using "import MyFramework"
and in Objective-C
you'll be using
"@import MyFramework;
(semicolon)".
In general we do want you
to be using @import
instead of #import.
For Swift- ...for frameworks
containing Swift code this is
absolutely required.
But even for other frameworks,
if they've been built
with Xcode 6 and have the
defined module build setting
turned on -- which
is on, by default --
then you have- ...then you'll
have a framework ready for use
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
then you have- ...then you'll
have a framework ready for use
in Objective-C or Swift.
The only reasons now to use
#import are if you are working
with an older framework that
has not yet been rebuilt
with Xcode 6 in a defined module
setting or if you're working
with C++, which does not
yet support the modules
feature we introduced last year.
So with that, everything
seems to be working
and our walkthrough is complete.
I'd like to hand
it back to Anders.
[ Applause ]
>> Thank you Jordan.
So, over the past hour we've
seen several different ways
of mixing Swift and Objective-C.
Swift is a better, safer
way to do Cocoa development.
It was designed to support
the Cocoa frameworks,
the Cocoa Touch idioms
and the common patterns
that we already know.
It's still Cocoa and
Cocoa Touch on iOS.
So that means that the
knowledge and experience
that you've gained
over the years
and the code you have
applies to Swift as well.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
and the code you have
applies to Swift as well.
And as we've seen
some of the patterns
such as optionally calling a
delegate method are much easier
in Swift and much more succinct.
And in other cases you've seen
how Swift makes us define our
intentions much more clearly,
which can help save us later
on so we don't just
accidentally message nil
or message an object
of the wrong type.
And another thing that I hope
you've seen here is we've put a
lot of effort into making
sure that Xcode takes care
of the details so that
when you're using Objective-C
your Swift code looks
like Objective-C and when you're
using Swift the Objective-C code
looks like Swift.
And you saw some examples of
subclassing Cocoa Touch classes,
but that equally applies to
your own classes as well.
So for more information about
this there is, of course,
the Swift book in
the iBooks Bookstore,
but I'd also especially
like to call your attention
to the Using Swift with Cocoa
and Objective-C document
that we have on
developer.apple.com.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
that we have on
developer.apple.com.
It's available as a PDF file
that you can download and read
on your favorite iOS
device and it goes
into a lot more details
about this.
There are several
related sessions.
One of them has already
been but it's on video.
It's the Introduction
to Swift, of course.
Later on today there's
going to be a session
on Swift Interoperability in
Depth and that goes into more
of the language details of how
to mix Objective-C with Swift.
There's a great session
on Creating Modern Cocoa Apps
coming up and another one
on Building Modern Frameworks
so that you can make sure
that you make your
frameworks embeddable on iOS
and useable from Swift.
Thanks and enjoy the
rest of the week.
[ Applause]