Transcript
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
[ Silence ]
[ Applause ]
>> Welcome.
So I'm Doug Gregor.
I'm an engineer on the Swift
Compiler Team, and we're here
to talk about Swift
Interoperabiity.
We're going to talk about a
couple of different things here.
So, of course, Swift is
a new language for Cocoa
and Cocoa Touch development.
Now, Cocoa's not
written in Swift.
It's written in Objective-C,
a language you've been using
for years and that all of
your apps are written in.
So, interoperability between
these two very different
programming languages
is absolutely critical.
So we're going to talk about
how that interoperating works
at the language level.
We're going to hit a number
of different topics today.
We're going to talk
about working with Cocoa,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
We're going to talk
about working with Cocoa,
seeing how the Cocoa APIs or
Objective-C APIs look and feel
in Swift and how to work
with them, as well as working
with some more Swift concepts
like dealing with AnyObject
and doing dynamic
checks on your types.
Then we're going to
talk about bridging
of the core Cocoa datatypes
and NSArray, NSDictionary,
NSString into their
Swift-native equivalents.
Then we'll move on
to subclassing,
so writing Swift classes that
subclass from Objective-C
and how they're mapped
back into Objective-C
so that you can use these
two languages together.
And, finally, we're going to
talk about Core Foundation
and Core Graphics, and
this general notion
of CF Interoperability within
the Swift programming language.
Let's get started talking
about working with Cocoa.
So Swift provides seamless
access to Objective-C APIs
through the Objective-C Module
System we introduced last year.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
through the Objective-C Module
System we introduced last year.
So you can pull your Objective-C
APIs whether they be from Cocoa
or your own into
Swift and use them.
And then Swift maps
those Objective-C APIs
into the Swift syntax.
This covers both the objective
parts of Objective-C -
the classes, protocols,
methods, and so on,
as well as the lower level
C things like functions,
enumerations, structs, pointers.
So you have access to all
of your Objective-C APIs.
Now when you look at one of
these Objective-C APIs in Swift,
it's going to be
different from Objective-C.
There are inherent
syntactic differences
between these two
language, of course.
Swift also has some
modern features that we use
when expressing those
Objective-C APIs in Swift
that will make it look
a little bit different,
as well as the bridging
of core Cocoa types
that I mentioned earlier.
Now, despite all of these
differences that you see
when looking at the
APIs, it's still Cocoa,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
when looking at the
APIs, it's still Cocoa,
it's still Cocoa Touch.
And the same conventions
and idioms still apply,
so what you know of
Cocoa works in Swift.
It's just a different
programming language
for the same great platform,
the same great frameworks.
So we're going to walk through
part of something we know
and love, and that's
the UIDocument class.
Here's a tiny slice
of it in Objective-C.
We're going to walk through how
and why that maps into Swift.
First thing, something simple, a
property: fileModificationDate.
It is an NSDate!.
This comes into Swift
as a property.
It's the var keyword.
The NSDate class, of course
just comes into Swift.
Nothing interesting there except
for this little exclamation
point that you may have noticed.
Now that exclamation point is an
Implicitly Unwrapped Optional.
What does that mean?
Well, let's look at
Swift in Objective-C.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Well, let's look at
Swift in Objective-C.
They're different languages with
some different ideas in them.
So in Swift, when
you have a value
of class type, so
I have an NSDate!
That can never be nil.
So a very strong constraint.
And it makes life a bit
simpler when you know
that thing is not nil.
Now, when you want to deal
with nil, you have an NSDate!
that could be nil, you
use an optional type,
and optional types are
covered extensively in the
"Intermediate Swift" talk.
We're going to cover them
a little bit more now.
That's the Swift side of things.
What about Objective-C?
Well, it does not have
the notion of a never
"never-nil" pointer
like we have in Swift.
And so we have a little
impedance mismatch here.
The Objective-C APIs don't have
the notion of this is not nil,
but we need to bring
them into Swift.
And so, we have the
implicitly unwrapped optional
with the exclamation point here.
And this gives us
a nice balance.
It means that we can
express the notion of nil
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
It means that we can
express the notion of nil
and you can test it against
nil to do those checks.
However, you can also just
directly access properties
or directly call a method
on it, or you can convert it
down to NSNil, and we'll unwrap
that optional object
automatically
for you doing the checking.
So it's a fairly syntactically
lightweight way of dealing
with nil in a language where nil
is a much more explicit entity
like in Swift.
Let's look at another property.
So here we have the fileType
property that's in NSString.
This is going to come into
Swift as a native Swift string.
Now, again, we have the
implicitly unwrapped optional
here so that nil can be passed
through since NSString doesn't
have a notion of nil inside it.
And there's a number
of Objective-C types
that get mapped slightly
differently into Swift.
So there's some very, very
fundamental types like BOOL
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
So there's some very, very
fundamental types like BOOL
and NSInteger that map into the
Bool and Int types within Swift.
So we're working with
all the Swift types.
There's id and Class,
which we're very familiar
with in Objective-C.
These map over to AnyObject!
and AnyClass!, something
we're going to talk
about in a couple of minutes.
And we also have the core
Cocoa types that are bridged,
like NSString and NSArray,
mapping to their
Swift-native equivalents.
Again, we'll talk about
that later in this talk.
Let's take a look at methods.
There's an Objective-C method
fileNameExtensionForType,
saveOperation.
It comes into Swift here
as, again, a method.
Now, one important thing
to know here is that all
of the selector pieces from
the Objective-C method are here
in the method's signature
in Swift.
The first selector piece has
become the so-called base name
of the method.
The second selector
piece, SaveOperation,
has become a label on
the second argument.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
has become a label on
the second argument.
A really important thing
here is that these labels
and their order are
enforced at the call site.
So, you must call it as
fileNameExtensionForType,
saveOperation, just
like you do in Cocoa
with the exact same
ordering, so to preserve
that nice readability from
Cocoa that we all know and love.
Now the other thing to note
here is the consistency here
on the Swift side.
All of the names and the
colons and the parentheses
and the commas are in
exactly the same places
in the declaration of
the method in the middle
and in the call site of
the method at the bottom.
So the kind of consistency
we like out
of building a new language.
Let's look at a little bit
more complicated method here
where we have some
blocks going on,
some more interesting things,
and map that into Swift.
And here there are two different
things I want to talk about.
The first thing I want to
talk about is the naming
of these argument labels and
the internal parameter names.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
of these argument labels and
the internal parameter names.
So here in Objective-C you
always have a selector piece,
goes before the colon.
And then you have the name of
the - at the internal parameter
that you use when you're
defining the method
in your .m file.
In Objective-C you always
have to write both of these,
of course, and many
times they're the same.
So you have some redundancy.
We do a little bit of syntax
optimization here in Swift,
so you just write the name once.
It serves both as the label
and the internal name.
If you want those names
to be different, fine,
we can handle that, too.
You just write the two
names next to each other.
The first one is the label
because that's what's important
for the caller to use.
And then the second one is the
internal name that you're going
to use within the
implementation of your method.
The next thing I want to
point out here is the Block.
So here we have a method
that takes a Block,
and Blocks in Objective-C get
mapped into Closures in Swift.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
You see, again, this is an
implicitly unwrapped optional
so that you can pass
a nil Block in here.
Now the really great thing
about getting Objective-C Blocks
mapped into Swift Closures is
that we get all of the great
closure syntax that is provided
by Swift, including
trailing closures
when your block is
the last parameter.
So all of your block-based
APIs that you've written,
all the ones from Cocoa, when
they're following the convention
of putting the block last get
this nice trailing closure
syntax in Swift.
Let's talk a little
bit about Initializers.
So in Objective-C
we have init methods
and init methods have a lot of
conventions built around them.
They start with the word "init."
They should be returning
instance type,
although that's a
fairly new invention.
So sometimes they're
still returning ID.
And when you're implementing
these things,
you have a lot of requirements.
You need to call "super init."
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
You need to reassign "self."
You need to check "self,"
you need to return "self."
So all of this screams, we
need something formalized
in the language.
And so Swift has this
notion of Initializers.
And we import Objective-C init
methods as Swift Initializers.
How do we get from the
top Objective-C code
to the Swift code in the bottom?
Well, we find the init,
so we match the init name
and the camel-case string here.
We actually look forward
a little bit to see
if it's really initWith
because that's extremely common.
And then we take the
rest of that Selector,
and lowercase the
first character in it,
and turn that into an argument
label for the Swift Initializer.
Now, why do we do this?
Well, let's look at
how we build objects
in Objective-C versus in Swift.
So in Objective-C you do
an alloc on your class
and then you immediately
send it an init message
to initialize the Object.
These two steps are
almost never separated.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
These two steps are
almost never separated.
Now in Swift we have
our Initializers
and we use this Unified
Object Construction syntax
where we write the
name of the Class
and then we pass arguments
directly to the Initializer.
And notice here, we're using the
argument label of fileURL to say
which Initializer
we're actually using
and then give it the argument.
And, of course, we folded the
alloc and the init together
in this one nice syntax
that also happens to work
for all other types of in
Swift whether they be structs
or enums.
Okay. So let's talk
about factory methods
because this is the other way
that we build Objects
in Objective-C.
So here we have something
from UIColor.
I've stepped away
from UIDocument.
And they have colorWithRed
blue green alpha.
And, of course, we
can go and construct
that by calling UIColor
colorWithRed green blue alpha.
All of this can be
directly imported in Swift.
It would just be a class
method, colorwithRed,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
It would just be a class
method, colorwithRed,
and green blue alpha
as argument labels,
and we could just
call it on the class.
This would be fine.
However, we don't really love
that they're two
completely different kinds
of initialization.
So we recognize the
common patterns
in how factory methods are
described in Objective-C,
and bring them in as
Swift Initializers.
And the really great
thing here is we get
that common Initialization
syntax for all
of these Objective-C APIs.
You don't have to think, "Is
there an init method for this?
Or is there a class
method for this?"
It's there as an Initializer.
Do you like that?
[ Applause ]
Let's go a little
bit down the stack
and let's talk about Enums.
So here's the
UIDocumentSaveOperation enum
as defined in Objective-C.
And if we look at this we see
a whole lot of redundancy.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
And if we look at this we see
a whole lot of redundancy.
This UIDocumentSave
prefix is used for the enum
and for both of its enum values.
Why is this?
Well, this is C.
The enum values in C are
in a global namespace.
We can't call these enum
values just ForCreating
and ForOverwriting because
that's going to stomp
on some other completely
different enumeration somewhere
else in the system
and cause havoc.
So we do this common
prefix by convention.
It helps code completion
find the right thing.
But when we're talking about
Swift, it's also a great cue
for us that we can do better.
And so we can import
this NS-ENUM
as a Swift enum chopping off
all of those common prefixes
to get us nice short
names for the cases.
Now the reason we can do this
is because the enum cases
in Swift are scoped within
the enum type itself.
How does this play
out in actual code?
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
How does this play
out in actual code?
Well, okay, if we call
fileNameExtensionForType
saveOperation, we can
refer to, say ForCreating,
with its fully dotted
name - class name.enum,
the same dotted syntax we use
for a number of anything
in Swift.
But Swift has type inference.
We know that this method takes
the UIDocumentSaveOperation,
so there's absolutely
no reason to write that.
You can just pass .ForCreating
and we will infer the
enum type from that.
I'd also like to
talk about NSError.
So this is our pattern in
Cocoa for dealing with errors.
And so there are many
methods throughout Cocoa
and throughout your own
apps that take an NSError ,
and that's a C pointer
to an NSError object.
We bring this in with a
special type in Swift.
In fact, if you type alias
for a much longer type
name call NSErrorPointer.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
for a much longer type
name call NSErrorPointer.
We're going
to see NSErrorPointer
twice in this talk.
For now we're going to
talk about how to use it
when we're calling into the API.
And it's not actually all that
much different from Objective-C.
So, if we bring this up, we
declare a local variable.
It's called error.
It's a type NSError optional.
And we pass its address
in when we're calling
contentsForType error.
And so in this code we check
whether we're getting back some
contents from this.
Then we can deal
with those contents.
If we fail to find any contents
well, we probably have an error.
So we can go unwrap
that optional error
and present it to the user.
And if we fall through the
else here, now we're in trouble
because something failed and we
have nothing we can do about it.
We hope that doesn't happen.
But this is the pattern
you'll be using when dealing
with NSError in Swift.
If you truly don't
care about the error,
you can also pass nil.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
you can also pass nil.
So we've walked through
a lot of little pieces
of the Objective-C
mapping into Swift.
And there are a lot of
rules that we've talked
about that you're certainly
not going to remember.
That's perfectly fine.
Xcode has your back here.
Use the tools to help you.
So if you're in Xcode,
you're in some Swift Code,
you can Command+Click
on a class name.
And we'll show you
the Swift projection
of the underlying
Objective-C class.
So take your favorite
Cocoa class and look
at how it maps into Swift.
Get a feel for the
language, an intuitive feel
for how these languages
work together and you'll get
into Swift really fast.
And the great thing
is, all of these tools,
all the rules I've talked
about, apply equally
to any Objective-C API
when it comes into Swift.
It doesn't matter if it's Cocoa.
It doesn't matter if
it's your own API.
The same rules apply, and you
can view your own Objective-C
APIs in Swift to get
to know them better.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
APIs in Swift to get
to know them better.
Now this works best
when you're following
"modern" Objective-C practices.
So these are using features
like Properties, instancetype,
marking your enumerations
with NS-ENUM or NS-OPTIONS
to describe more
semantic information
about what these enums
actually mean in C.
We've also introduced
NS-DESIGNATED-INITIALIZER this
year to mark your
designated Initializers
and formalize a Designated
Initializer pattern,
both in Objective-C
through additional warnings,
and as the initialization
model for Swift.
So, of course, we want
you to follow all of these
"modern" Objective-C
practices but we don't want you
to have to go it alone.
And so this year we introduced
the Objective-C Modernizer
that helps find these
cases in your code
where we could possibly
modernize them to work
with all these "modern"
Objective-C features
and give you a better
projection into Swift.
And that Modernizer is
discussed in the "What's New
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
And that Modernizer is
discussed in the "What's New
in LLVM" talk earlier today.
Highly recommend you catch
it on video if you missed it.
With that, let's talk about id.
What is id in Objective-C?
It's kind of a placeholder
in some sense.
It means, I have a value.
I know it's an object
but I don't know
or I don't care what the
static type of that object is.
It's going to vary at
runtime most likely.
And there's a couple
of core operations
that you can perform on id.
You can do upcasting on it.
So, if I have this
variable object of type id,
I can put an NSURL into it.
Later I can go reassign it
and I can put a UIView on it.
That's perfectly fine.
They can both be upcasted
essentially to id.
They're both objects.
I can do Message sends
to id just directly
by doing a Message Send.
I can subscript an id
if I really feel like.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
I can subscript an id
if I really feel like.
In Swift, any object is
the equivalent to id.
And it provides these same
core operations - Upcasting,
Message Sends, Subscripting -
that you can do on
id in Objective-C.
Now one of the things we know
from using id in Objective-C is
that you sometimes have
to be a little bit careful
because if you send a
message to an object
that doesn't have a
corresponding method,
you're going to get
a runtime failure
that this is an unrecognized
selector.
Now in Objective-C we
have an answer for this.
Using this respondsToSelector
idiom.
Do an if. Check whether it
respondsToSelector, then do it.
In Swift we like to do
a little bit better.
So let's take the same call
and let's do a
removeFromSuperview() call
on this object.
And the thing to note here
is that removeFromSuperview()
on an object of unknown type,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
on an object of unknown type,
it may be there, it
may not be there.
Well, how do we deal with
this notion in Swift?
We use an "optional" that says
there may be a value there;
there may not be.
And so the reference to
removeFromSuperview()
on object is in effect,
optional.
That means we can use the
optional Chaining Operator here
to -
[ Applause ]
What we're doing, of course,
is we're folding the
respondsToSelector check in,
so we do the reference, go
look for RemoveFromSuperview.
If it's there, go on, call it.
If it's not there, stop
evaluating this expression.
We're done.
Now, something that id does
that AnyObject does not
do is implicitly downcast.
So I have Object, which is a
type AnyObject, and I'm trying
to assign it into a UIView.
This is going to
produce a compiler error
because this is a
unsafe downcast.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
because this is a
unsafe downcast.
How do we deal with this?
Well, there's really
two cases here that need
that you need to think about.
One case is, I know it's a
UIView but for some reason
that strong type information
got lost when going
through some API somewhere.
If I know for sure
it's a UIView,
I can use the cast
operator, "as", to say,
"Treat this object as a UIView."
We're going to do these kind
of class check at runtime
to make sure that's
absolutely true.
But the type system will
believe you at this point.
Now if you don't know whether
this object is a UIView
or not, you can use the "as?"
to perform a conditional
downcast.
[ Applause ]
Think you guys have
figured it out?
But just to be sure, this is
doing "is kind of" class check.
And it's wrapping the result
in an optional UIView.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
And it's wrapping the result
in an optional UIView.
It's nil if the check failed.
It has the UIView if
the check succeeded.
We can do an if-let here
to completely do this
entire thing safely,
and view in here is the
UIView we were looking for.
So let's take a little
bit of detour and talk
about tiny bit of protocols.
Here's an Objective-C Protocol
for a UITableViewDataSource
and its Swift equivalent.
Not a whole lot new here.
But there are two things that
I do I want to point out.
The first thing I want to point
out is optional and required.
So in Objective-C optional
and required are essentially
modes in the protocol.
You say @optional,
and everything
that follows is optional
up until the point
where you hit an @required and
then everything is required.
And we're not totally thrilled
with this decision now.
And the basic reason is
that you can't just look
at one single declaration
in the protocol
and know whether it's
optional or required.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
and know whether it's
optional or required.
You have to go scan up
your protocol to find it.
And so we did something a
little bit different in Swift
in that requirements in
protocol are required
by default in Swift.
If you want to make them
optional, then tag them
with the optional attribute
to make them optional.
The other thing I want
to point out here.
We're doing a little bit
of Protocol Inheritance
and we're inheriting
from NSObjectProtocol.
So in Objective-C we will
have NSObject the class
and NSObject the protocol.
And they have the same name so
we have to add the "the class"
or "the protocol" at the
end when we talk about it.
The language keeps these in
syntactically distinct points
so the language isn't confused.
But in Swift we wanted
to bring all these things
into the same namespace because
that's far more convenient
for the general case.
And so we needed to
rename something.
And essentially when there's a
conflict between a class name
and a protocol name,
we'll append protocol
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
and a protocol name,
we'll append protocol
to the name of the protocol.
Why did we do this?
Well, let's take a look at
another use of id that we see
in Objective-C, and that's
protocol-qualified id.
So this dataSource here is an
object of some unknown type.
But we know that
the type conforms
to the UITableViewDataSource
protocol.
We describe that a
little bit more directly
in the Swift language by
just saying the dataSource is
a UITableViewDataSource!.
That's it.
Now some of you here noticed
with protocol-qualified id,
you can have many different
protocols if you want.
We can use the protocol
keyword with angle brackets
to describe more
than one protocol.
Now, one of the things we do
with Protocol Conformance is we
have an object of unknown type
and we want to determine, does
it conform to the protocol?
This is the "conforms
to protocol check"
in the Objective-C runtime.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
in the Objective-C runtime.
In Swift, we do this same thing
with the conditional
downcast operator.
So we can just ask, is my object
the UITableViewDataSource"
conforms to protocol check,
happens in the runtime,
captures the results
in an optional.
Here we can go easily do
that, call one of the methods
and compute the number of
rows in the first section
of this TableViewDataSource.
Let's make our example a
little bit more interesting.
Let's compute the number of
rows in the last section.
So here we need to compute the
number of sections that exist
in the TableView, subtract one
off of it, and then we can ask
for the number of
rows in that section.
Now there's a problem
with this code.
And the problem is the number
of sections in TableView,
as you might remember,
is an optional method.
It might not be there
at runtime.
So we're going to need to
compile error out of this
because we need to deal
with the optionality
of this protocol method.
And we deal with this
the same way we deal
with optionality everywhere
else in the Swift language,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
with optionality everywhere
else in the Swift language,
using the mechanisms we have.
So here we're going to
use the chaining "?"
operator. We're checking, does
my DataSource have a number
of sections in TableView method?
If so, call it, given
the TableView,
and then we capture the
result in numSections
so we can compute the
last section number
and get the number of rows
in the last section
of our TableView.
That's about all we're going to
talk about with Protocols here.
If you're interested
in Protocols and some
of the amazing things
they can do,
there's an "Advanced Swift"
talk tomorrow morning.
It goes into more depth on
those and their interaction
with the generic system.
So, wrapping up here, AnyObject
is Swift's equivalent to id.
The functionality is similar.
The ideas are similar
and the uses are similar.
However, it's more
safe by default.
Now we didn't talk about it,
but there's also AnyClass,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Now we didn't talk about it,
but there's also AnyClass,
which is Swift's equivalent
to class and has most
of the same behaviors.
Now the other thing that we've
seen is how Optionals are used
throughout the language to
represent dynamic checks.
We've taken "is kind
of" class checks,
conforms to protocol checks,
responds to selector checks,
and folded them all into
the notion of Optionals
within the language with
their optimized syntax
to make them easy to use and
easy to do the right thing.
With that, let's switch
gears a little bit and talk
about Bridging of
Cocoa data types.
Now first, let's talk a little
bit about the native Strings,
Arrays, and the Dictionaries
within Swift.
The goal of Swift
is to have one set
of general-purpose native
value types that you use
for nearly everything.
These need to be
safe by default.
This means bounds
restricting for arrays,
automatic memory
management, and so on.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
automatic memory
management, and so on.
They need to have predictable
performance so that you can look
at code and have a sense
of how it's going to behave
with no surprises, how
it's going to perform.
And, of course, we want
arrays and dictionaries
to be collections and they need
to be strongly typed collections
that work with any type.
We can't limit them
just to objects
because sometimes you need
an array of strings or ints.
And we don't have a seed to
fall back to for the cases
where the other tools
don't work.
This is it.
Now, to support having
this one notion of one set
of general purpose native value
types, we're going to bridge
from Cocoa's NSString,
NSArray, NSDictionary,
into the Swift-native
equivalents.
So let's first talk a little bit
about the native
string type itself.
So, String is an efficient,
Unicode-compliant string type.
Core string type of Swift
has Unicode built in through
and through so it makes
it easy to work with.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
and through so it makes
it easy to work with.
We provide flexible
and efficient high-level
APIs to work with strings.
You can easily do
concatenation, searches,
prefix matches, sub-strings.
And the strings provide
value semantics,
which makes them
easier to work with.
And value semantics is generally
a fairly simple notion of,
you know, if I have two
variables of string type,
modifying one of them doesn't
affect the other one, all right.
This is very nice for a
fundamental data type.
Now, of course, you can also
think of strings as a unit,
but you can also think
of them as being composed
of characters which,
in fact, they are.
And so we can go walk over a
string and using the for loop,
and get all of the
characters out of the string.
And you get the answer
that you would expect.
There are five characters here
even though there's no emoji
at the end.
So I want to talk a little bit
about characters and code points
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
So I want to talk a little bit
about characters and code points
because the character
that you're getting
out of here is a full
Unicode character.
It's not a UTF-8 code
point or UTF-16 code point
that you have to deal with.
It is a Unicode character.
And now, one of the challenges
with the Unicode characters is,
you really can't encode
them efficiently in a way
that treats a string as
just an array of characters.
It would be too large.
And so what you generally see is
that a string is encoded
as, say, UTF-8 or UTF-16.
But working with those
UTF-8 or UTF-16 code points,
that requires Unicode expertise
to get right all the time.
And so we made a really
interesting decision here.
We decided not to provide the
super low-level operations
like length and characterAtIndex
to let you poke directly
at the UTF-16 or UTF-8 code
points, or whatever is stored
in the string because doing
so causes big problems.
Instead we want you to
use the high-level APIs
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Instead we want you to
use the high-level APIs
and let the library do
the hard work of dealing
with all the intricacies
of Unicode.
Of course, there's still common
operations you want to use.
You may want to count the number
of characters in a string,
so there's this countElements
algorithm.
It works on any sequence
and allows you to, well,
just count the number of
characters in a string,
and this produces
the right answer,
which is there are five
characters in this string.
Some of you will want to
work with code points, right.
You may be Unicode experts
and that's wonderful.
You can get access
to the code points.
There's a property UTF-16 that
gives you a lazily computed view
on the string producing
the UTF-16 code points
in that string.
And we can go walk over
the 16-bit unsigned integer
code points.
We can print out the number of
code points here and, of course,
you'll get the answer "6"
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
you'll get the answer "6"
because there are six UTF-16
code points in this string.
The last thing I want to talk
about with strings
is the relationship
between string and NSSring.
So NSString has a wealth
of really great text
processing APIs
that you've probably
been using for years.
So we've made all those
Foundation APIs directly
available on the string
type, so the APIs you know
and love are there,
and you can use them.
Now in doing so, we've made
them a little bit more Swift.
We've tightened up the
type signatures so that
if you're going to split a
string into its components,
well, you're getting it back
an array of strings rather
than just an array
of somethings.
Now you may have developed
your own categories on NSString
with additional functionality
that you use
within your own applications.
You can get to those
with a simple Cast.
So you can take a Swift string,
turn it into an NSString,
so this is just a conversion,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
so this is just a conversion,
and then call your
NSStringMethod.
If you find yourselves
doing this a lot,
feel free to just go ahead
and extend the underlying
string type.
Add your StringMethod,
make it a little more Swift
with strong type signatures,
closures if you'd like.
But this should help you feel
at home in Swift fairly quickly
and use the String type.
Now let's move from
String to a container.
Let's talk about Arrays.
So here we have toolbar
items that is in an NSArray.
That's going to come into
Swift as an array of AnyObject.
Now these two types
are fairly similar.
You can iterate over them
and what you're going to get
out of it are values
of type AnyObject.
They're objects but you don't
know what kind of object it is.
You can subscript into
them and, of course,
you will get an AnyObject.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Now, in Swift, you tend to deal
in typed arrays more often.
And so there are
some other operations
that the core language
needs to provide
for you to make this clean.
So maybe I'm composing my
toolbar items into a Swift array
and that Swift array is going
to contain UIBarButtonItems.
That's what actually goes into
the toolbar items property.
I can work with that Swift
Array and then I can assign it
into the AnyObject array.
So this is essentially doing
a safe upcast of any array
of ToolbarItems to any
array of AnyObjects.
It also happens to be
calling into Objective-C,
which we'll get to in a minute.
Now we also see the flip side
of this where we want to, say,
iterate over all
the ToolbarItems
in this particular view
controller and here we're going
to get AnyObject values and,
we talked about cast earlier,
so we can downcast each of them.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
so we can downcast each of them.
This is fine but it's a little
bit on the tedious side.
And so we have specialized
syntax here
to downcast an entire
array at a time doing the
"is kind of" class
checks necessary
to make this safe
lazily behind the scenes.
And then you can walk
over all with them.
[ Applause ]
Now, we've seen NSArrays
in the Objective-C side,
Swift arrays on the Swift side.
Let's take a little bit
of a peek under the hood
at how this actually
works because you're going
to be writing a lot of Swift
code that interacts with a lot
of Objective-C code and we
want this to perform well.
So there's a Swift array.
And the Swift array actually has
two internal representations.
Its first representation is
probably what you'd expect
out of Swift.
It's a native representation.
It has a length, which is the
number of elements in the array.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
It has a length, which is the
number of elements in the array.
It has a capacity that's used
so we can algorithmically
efficiently add things
to the array.
And then it has the buffer of
elements that are in the array.
And, of course, those buffered
elements, whatever kind
of array it is, that's how
much storage they take.
If we have an array
of 32-bit integers,
each element takes
32 bits of storage.
There's not extra
boxing going on here,
no extra performance loss.
It's just a native buffer.
Now we also have this
second representation
and we do a couple of pointer
tricks so that we can fit it
into just a tiny
amount of memory.
And that's the Cocoa
representation.
So any Swift array can actually
be an NSArray underneath
as representation.
And all the operations
on an array handle both
of these representations.
So if I subscript my array and
it happens to be an NSArray,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
So if I subscript my array and
it happens to be an NSArray,
we'll use object and
index behind the scenes.
So you get the result
that you want.
If we do append to
such an array, well,
we can flip the representation
quickly and give you
that efficient append operation.
So given these two
representations,
we can talk about the notion
of bridging, of converting
between an NSArray, as
Objective-C would see,
and a Swift array that
you use within Swift.
There's two directions here:
going to Objective-C
and coming back.
So first, let's talk
about coming back.
So we have an Objective-C
method.
In this case, it's the
getter for toolbar items.
In Objective-C, that
returns NSArray.
In Swift, that's going to come
back as an array of AnyObject.
How do we do this?
Well, given our two
representations,
it's extremely efficient
to do it
because we have our
representation
that can just take
that NSArray directly.
All we need to do is
one copy operation
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
All we need to do is
one copy operation
to make sure the contents
don't change underneath us
if it's a mutable array.
But the common case here is
that it's not a mutable array.
It's an immutable NSArray and so
this copy operation is trivial.
It's a message send, it's
a retain, and that's it.
So its conversion
is extremely fast.
Let's talk about
the other direction,
going from a Swift
array to an NSArray.
So this would happen when we
take, say, our ToolbarItems.
It's a Swift array.
And we call the Objective-C
setter,
which expects an NSArray.
Well, now we have an
interesting question
because there's two possible
representations here.
There's the really easy answer.
It's already in the
Cocoa representation
and we can just hand off
the NSArray, no work at all.
But the native one, that a
different question entirely.
We could copy the whole buffer
but that would be awful.
We could possibly go
allocate a little shim object.
That's also possible.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
That's also possible.
Instead, we decided to make
our native representation
into a little bit of an
Objective-C object [laughter].
It's already an NSArray.
And we've optimized
the allocation here
so we can build these
objects super fast
and just pass off our
native representation
as if it were an NSArray
and it works beautifully
on the Objective-C side.
[ Applause ]
I think that's enough
of Bridging.
Let's talk about subclassing.
Okay. So, Swift objects are
all Objective-C objects.
Now what this means is that,
if you define a class in Swift,
it has basic Objective-C
interoperability built in.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
We use the same layout as an
Objective-C class so there's an
"isa" pointer in there.
The "isa" pointer points
out to Objective-C metadata.
There's the same
underlying infrastructure,
the thing that makes "arch" work
and the basic frameworks work
with retain and release.
You can expect a class.
That sort of thing.
They're all available.
Now, if you really want to
make use of your classes
from within your
Objective-C code, well,
then you should inherit
from an Objective-C class,
whether it's NSObject
or some other class.
And this is going to expose your
class to the Objective-C world
and make all the things you
write in Swift available also
to your Objective-C code.
So we're going to continue
with our UIDocument example
from earlier and we're going to
create a little MyDocument class
that inherits from UIDocument.
And we're going to
talk about a couple
of the things that
subclasses do.
They override methods.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
They override methods.
So here we're going to
override the handleError,
userInteractionPermitted method.
And you do this exactly
the same way as you'd do it
if you were overriding
a Swift method.
It doesn't matter.
The syntax is the same.
The fact that the super class
is written in Objective-C?
Completely irrelevant to
the syntax of the language
and how you work with it.
Now one thing to note
in Swift is that the
"override" keyword is mandatory.
Why do we do that?
There's a couple of
reasons for doing that.
One of the reasons is because
when you look at a method,
you probably want to know
if the intention here is
to override your
super class's behavior
because that's a really
important part of your API.
It's a really part
of understanding what this
method is supposed to do.
Now the other thing it does is
it helps overriding accidents.
For example, I meant to override
something from my super class,
but I typed part of
the selector wrong
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
but I typed part of
the selector wrong
and the method name
no longer overrides.
My code isn't running
and I have no idea why.
Well, with mandatory
override, we catch that.
If you didn't override something
and you thought you did,
compiler will complain.
There's also the real
surprise which is
when you override something
from your super class
that you didn't even
know existed.
And this is the case where you
just wrote a method and maybe
in this release that
method happens to exist
or maybe it doesn't
exist in this release,
but some joker added it to the
next release in the frameworks
that you use and now
you're overriding something
that you didn't know
existed at the time.
We can catch that by
requiring override throughout
the language.
We could also talk a little bit
about overriding properties.
So, in Swift, you don't
override the getter
or the setter separately.
Instead you override
the property itself
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Instead you override
the property itself
and then you provide a getter
or a setter as appropriate.
So here we're doing
something very simple.
We're overriding the
description property
and providing a new
getter for it.
Now what this means to
the Objective-C runtime,
to your Objective-C code,
is that you've overridden
the getter.
But the semantic model
in Swift is different.
It's based on overriding the
actual thing that was declared.
In this case, the property.
As I mentioned before,
NSError is going to come back.
So we have contentsForType
error we're going
to override in our subclass.
And remember that the
Objective-C method took NSError,
a C pointer to an NSError
which could be nil.
The way we work with
these in Swift is
that the NSErrorPointer class
provides a couple of operations
that you would expect
out of a pointer.
You can test it for nil, as
we do in the if check here,
to see whether we were actually
given a valid point error
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
to see whether we were actually
given a valid point error
where it's being given
nil by our caller.
Now, if it's not
nil, we can point
at the memory location
associated with that pointer
by referring to it as
error.memory and we can read
from that memory or
write to that memory
by just reading or
signing to it.
Now, and this error pointer
is going to take care
of the nitty-gritty details
of making this auto-releasing
to fit in with the
code conventions
of Cocoa NSError handling.
So it's actually fairly easy
to deal with the C pointer
from within the Swift world.
Now let's take a look
at the Swift class
that we've been building here.
We have MyDocument.
It inherits from UIDocument.
It has a property in it.
It has some method overrides.
It's just a Swift class
through and through.
But all of this is
accessible directly
in your Objective-C code.
So if you project
this into Objective-C,
it would look like this.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
it would look like this.
All the same elements are there.
We have properties.
We have methods.
Now there's some interesting
things to point out.
Well, for one, we have
this item's property
that is in an NSArray.
Remember, we talked
about bridging here.
The original Swift code
had an array of strings.
We bridged that seamlessly
over to an NSArray
that contains NSString objects
for your Objective-C
code to use.
So you can use strong typing
in the Swift world and it maps
over to the natural thing
within the Objective-C world.
The other thing I want to point
out is this really ugly name
up top you've noticed.
So this is a mangled name.
Usually you're going to see
this as MyApp.MyDocument,
unless you're poking at
the internal somewhere.
And the purpose of this mangled
name is to put everything
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
And the purpose of this mangled
name is to put everything
in a namespace of some
sort so you don't have
to prefix all of
your class names.
Instead, what Swift does
is it puts the module name,
which is your target, your
framework, or your app -
that name into the names
of the classes it creates
so you can use the
simple names that you want
to use throughout your
application and not worry
about a conflict with something
else in the system somewhere.
[ Applause ]
When you're writing
your Swift Classes,
you do need to be a
little bit cognizant
of the limits of Objective-C.
Swift has a lot of cool
features; you might want
to use them -Tuples,
Generics and so on.
And if you go crazy
in your classes
and you use these features,
you might be a little surprised
that this generic method that
returns a tuple doesn't show
up in your Objective-C code
because Objective-C has no way
to express that signature.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
There's nothing we can do.
We don't have tuples
in Objective-C.
So if this happens to you
and you're surprised by it,
there's an attribute you
can add to your method.
It's the objc attribute.
And what this does is it
asks the compiler to check
and make sure that this method
or property or initializer
or whatever is expressible in
Objective-C so it can be used
from your Objective-C code.
And if it's not expressible in
Objective-C for some reason,
the compiler will
give you a hard error
to tell you this is not
something you can use
from Objective-C.
Now the objc attribute
actually has a second purpose.
And that's controlling
the names that you see
in the Objective-C
side of things.
So, here's a property enabled.
It has a getter.
It has a setter.
In Objective-C this is going to
come through as a property named
"enabled," a getter named
"enabled," and a setter named
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
"enabled," a getter named
"enabled," and a setter named
"setEnabled:" That's
not Cocoa convention.
You'd really rather the call,
the getter, "isEnabled".
And so to do that we just
use the objc attribute,
provide it with the
selector "isEnabled"
so we can control
the mapping ourselves
between these two languages.
I don't expect you
to do this often,
but it's there if you need it.
You can also do this
for the name of a class.
And what we have here
in the parentheses,
in the objc attribute, is the
non-named space name of a class.
So why would you do this?
Well, perhaps you're porting
part of your application
from Objective-C to
Swift for some reason.
And so you had ABCMyDocument.
Now you just want to
call it MyDocument
because you're sick
of typing ABC.
However, you have some
archives that you still want
to have work, because
this is going
to be a drop-in compatible
implementation.
In Swift, for your
Objective-C class,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
In Swift, for your
Objective-C class,
you can use the objc attribute
to give this class the
runtime name of ABCMyDocument
and keep all of your
archives working.
One last thing I
promised to talk about,
and that's CF Interoperability.
So by CF we're referring
to all of the C-like APIs
that work with the C objects.
So, this is Core Foundation.
This is Core Graphics
and other frameworks,
maybe some of your
own frameworks.
And let's take a
little look at using CF,
particularly Core
Graphics in Objective-C.
I'm going to do something
really simple here.
I'm going to draw a gradient in
a rectangle using Core Graphics.
Here's my start.
I need to go build
up the ColorSpace
and build up the Gradient.
Now there's a couple of things
of here that I find non-optimal.
So the one thing
is bridge casts.
So we're in ARC.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
It's partly great.
It's handling our
NSArray for us.
We're using the nice
array literal syntax.
But now we need to do bridge
casts between CGColorRef and id
so we can put things
into the NSArray.
And we have this
CFArray cast sort
of doing toll-free
bridging there
between the NSArray
and the CFArray.
And there's also this
semi-amusing thing
that we're using three
different kinds of arrays
in four lines of code.
And you can write this - you
can try to write this better.
I couldn't, actually, find a way
to make it cleaner than this.
And it's really unfortunate
because the NSArray gives
us some useful behavior.
ARC is managing its
lifetime for us.
That's great.
We need the C Array because
we need to put CGFloats in it,
and we can't do that
within NSArray.
And finally, we need to
do the toll-free bridging
over to CFArrayRef
because that's what we use
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
over to CFArrayRef
because that's what we use
with Core Graphics APIs.
Now, moving along, we
can create some points
with CGPointMake and, of course,
even though you're under ARC
where memory management
is automatic,
it's not automated
for CF things.
So we have to remember
to release the ColorSpace
and release the Gradient.
We feel like we can
do a little bit better
in the world of Swift.
So let's start again,
this time in Swift.
And first, let's
build our colorSpace.
So here we're just calling
CGColorSpaceCreateDeviceRGB().
Nothing different about that.
However, the type that we infer
for this ColorSpace
variable is CGColorSpace.
Note the lack of a ref
at the end of this.
This isn't some opaque pointer.
This is the CGColorSpace
class that we've created.
What's the nice thing
about being a class?
Well, that means we're in
the ARC model and we're going
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Well, that means we're in
the ARC model and we're going
to automatically manage
the memory for you.
[ Applause ]
Let's go a little
further and create
that Gradient we talked about.
So here, remember, we need to
pass a couple of arrays through.
We can use this nice
Swift array literal syntax
to form an array containing
startColor and Color.
We're doing all of the bridging
automatically here for you.
So we've created
the NSArray we need.
We've toll-free bridged it to
the CFArray behind the scenes
so you don't have to deal
with the fact that there are
so many array types
running around.
Did the exact same
thing for the C parts.
So here we just have an array
of floating point values.
So it's treated as a native
Swift array of CGFloats
that we bridged seamlessly
to the underlying C array
that this C function expects.
Let's keep going with
our example here.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Let's keep going with
our example here.
CGPoint. You can use
CGPointMake if you want.
It's perfectly fine.
It works exactly the same way
as it does in Objective-C.
However, whenever
we import a struct,
like CGPoint is a struct, we
provide it with initializers
that have labeled arguments.
And so a better way to
build CGPoints in Swift is
to just construct a
CGPoint value using
that same construction
syntax we've been talking
about throughout this talk.
And then use the
argument labels x and y
to make it absolutely
clear what you're doing.
And this brings a
little bit of a flavor
of that nice Cocoa readability
using argument labels
into the underlying CF APIs.
And that's it for
our example in Swift.
It's smaller.
It's easier.
There's far fewer concepts
that you have to deal
with because we've automatically
taken over the management.
[ Applause ]
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
[ Applause ]
Now you may have some of
your own APIs that we refer
to as explicitly bridged.
So these are CF-like APIs
where we're not quite sure
whether you're following all the
CF memory conventions because,
unlike the world of Cocoa
which is fairly tame, and we've
been following conventions
fairly well for many years,
we haven't been following them
so well in C as a community.
And so we may have this
function GetRandomColor,
produces some random color.
When we pull this in, the Swift
compiler doesn't know whether we
can really trust that this
returns plus zero or not.
It has get in the name
but we're not sure.
And so we do the safe
thing and we import it
as an Unmanaged
which means we can't directly
manage the memory here
because we don't know
what the conventions are.
So what is this Unmanaged thing?
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
So what is this Unmanaged thing?
So Unmanaged is actually
a generic struct
over an arbitrary T.
Now the details of generic
structs we don't need
to go into now.
They're covered in the Advanced
talk which I highly recommend.
What we want to look at right
now is just the simple API
of this Unmanaged type.
We have two core operations
- takeUnretainedValue,
which you use for +0
returns, and takeRetainedValue
which you use for +1 returns.
Now, when we call our
CGColorGetRandomColor,
we want to immediately use
one of these two functions.
So we know that GetRandomColor
returns a plus zero,
so we're going to do a
takeUnretainedValue of it.
Now the reason to
do this immediately
after the call is
this gets us a CGColor
which takes us right back
into automatically
managing memory for you.
So the window in which
you have to do something
with manual memory
management is tiny.
It's just this one
little line of code
where you establish
what the convention is
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
where you establish
what the convention is
for CGColorGetRandomColor.
Now, if you own
CGColorGetRandomColor,
you can audit your APIs to
make sure they follow the Core
Foundation naming conventions
for memory management.
And when they do, you can
use these annotations here
in core foundation -
CF-IMPLICIT-BRIDGING-ENABLED
and DISABLED.
Put this over a whole header
once you've audited all the
methods in that header.
And when you do that, well,
now your function when you use
from Swift, gets you right back
into the managed world directly.
So you have the great
automatic memory management
that Swift can provide for CF.
So let's wrap up here.
We've talked about a
lot of different topics.
We've talked about
interoperability
between Swift and Objective-C.
We've talked about a whole lot
of rules about how that works.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
We've talked about a whole lot
of rules about how that works.
But let the tools and
documentation help you.
They can show you Swift and
Objective-C side-by-side
so you can get a feel for how
your Objective-C APIs work
in Swift.
We've talked about
some of the details
of bridging Core
Cocoa data types
and using Swift's native types.
And we talked about automated
CF memory management available
in Swift.
For more information,
check out the
"Swift Programming Language
Book," and also the "Using Swift
with Cocoa and Objective-C"
guide that goes
into more details
on the interplay
between these two
programming languages.
Thank you.
[ Applause ]