WWDC2016 Session 414

Transcript

[ Music ]
[ Applause ]
>> Hi everyone, welcome to using
and extending the
Xcode source editor.
I'm Mike and I work on Xcode
and I am the only thing standing
between you and the
beer bash [laughter].
So what are we talking
about today?
Well I'm going to start off and
show you some great new features
that we've added to Xcode
8, but that's not all.
I'd also like to show you
some really helpful features
that are already in the
existing shipping Xcode today
that I think you probably
don't even know about
but I think they're really cool.
And I use them every
day to quickly edit
and navigate the
Xcode source base.
and navigate the
Xcode source base.
And just to be clear,
everything I'm going
to show you comes
standard built into Xcode.
Then, my colleague Chris,
he's going to come up
and show you how to go
beyond just what we offer
and further extend the Xcode
source editor yourself.
You can write the extensions
to make the editor do some
of the things you've
always wanted it to do
and then you can even
share those extensions
with your friends,
your co-workers,
or even the whole world.
The first thing you'll
notice in the source editor,
or if you just looked up and
around today at the conference,
is our new source
code font, SF mono.
This is the mono
space compliment
to our system font
San Francisco.
Our font experts have
explicitly designed SF mono
for retina displays.
And have carefully designed
each of the glyphs used
for code punctuation
to be distinct
and legible, even
at small sizes.
The next thing you'll notice
as I'm just moving around here
in the editor, is that we have
this new current line highlight
in the editor, is that we have
this new current line highlight
that shows you where
you're at, at a glance.
Each of our built-in
Xcode themes has a subtle,
hand tuned highlight color,
but it's also customizable.
So you can change it to be
as bold as you like or set it
to completely transparent
if it's not your cup of tea.
So you're probably already
familiar with our comment
and uncomment command,
command/ right?
Well we've added a new command
that you can get to just
by dropping the option
key along with command/
and that inserts a
documentation comment.
[ Applause ]
So, this works directly on or
above any method, class, struct,
anything that you know is
a structural code element.
And, you'll notice that these
little placeholders here,
give you a chance to
describe, you know your method,
give you a chance to
describe, you know your method,
or discuss the parameters, the,
whoops, the throws description
or you know, say
something noteworthy
about the return value.
And what's really valuable is
that these bits of documentation
that you add in the code
docs show up in quick help
and at the bottom of the
code complete, window.
And also notice that we've
provided our new SF mono font
with more weights than
just regular and bold.
The documentation comment uses
SF mono light while the keywords
use SF mono medium which
are just a little lighter
and just a little heavier
than the SF mono regular
that the rest of the file is in.
Unlike most other
mono space fonts,
we've given SF mono a full
spectrum of weights from light
through semi-bold,
all the way to heavy
and we've included
the italic variance.
This keeps the spacing between
each character even while
letting you customize exactly
how much emphasis you want.
So in the next feature I'm going
to show you I'm going to jump
to another file, I'm
going to use open quickly
to another file, I'm
going to use open quickly
by pressing Command, Shift O,
and typing in a few characters
to get to my
TimerViewController.
And you may have
seen this before,
this is the document items
menu, and you can open this
by pressing Control 6.
It has all of the classes,
structs, enums, properties,
everything that's
inside of that file.
But that's not really
the most interesting bit.
Did you know that if you just
start typing while this menu is
up, it filters the items?
And you'll notice, here that,
this little fix me comment
that I have, it actually
has this little Band-Aid,
bandage icon on it,
which I thought was kind
of cute [laughter].
So, jumping here to my load view
method, and you can see here
that we have an image in a
color that we've been waiting
for a designer to deliver to us.
But I actually got those P&G's
last night and I put them
But I actually got those P&G's
last night and I put them
into the jogger asset catalog,
which we can actually see
down here in the library.
So this is for the Timer Button.
So I'm going to go and I'm
going to select to the end
of the line here by
pressing Control E,
while holding down Shift.
And I'm going to
invoke code completion
by pressing Command Space.
Now watch carefully, I'm going
to assign this image directly
by using code completion.
I'm just going to type t,
b, to get my Timer Button.
And you'll also notice
that code completion
as in Xcode 7.3 now uses the
same fuzzy matching algorithm
that we use for open quickly,
the document items menu,
and even our new search in
the documentation window.
And you can see here on the left
side, we show a little preview
of the image so you know
that you're picking
exactly the right one.
So, boom right there,
in your source,
this is an image literal.
[ Applause ]
So every image in the library,
regardless of whether it comes
from an asset catalog or it's
just referenced in the project,
it's available from the
code complete window now
in the source editor.
So, the way this works is we
insert a Swift specific entity
into the source which is
known to the Swift compiler,
and it dereferences out to a
call to UIImage image mamed.
So you can see all of
your images now in place,
instead of just the name string.
And we have this for colors too.
So I'm just going to select
the end of the line here,
and invoke code completion
and type color.
And what this will,
when I hit Return here,
you notice that I get this
really neat color picker pop-up.
From here I can just arrow
down, and pick a color
like this, lovely green here.
And what's really important to
know is that all of these image
and color literals are the,
they're fully keyboard
navigable.
they're fully keyboard
navigable.
If I just hold down Shift and
arrow back over the literal,
and hit Return, I bring up
the color picker pop-up again.
And, you know to which I
can still arrow around,
but if these colors that are
in the pop-up aren't exactly
what you're looking for,
and there, it's not exactly
what I'm looking for,
you can use the recent
colors, which are also shared
with interface builder, or you
can click on the Other button
down here and it brings up the
standard system color panel.
So in this case, I
actually just want to sample
out this lovely navy blue,
right from the button
since this is going
to go with the button
as I'm constructing it.
But we still have a little
bit of an error here,
the color literal is, is a
UIColor here, but the layer
that we're assigning
this to is a cgLayer
and it's expecting a cgColor.
So I can fix that
real quick, like that.
[ Applause ]
Hopefully this makes it
really clear, that image
Hopefully this makes it
really clear, that image
and color literals are really
the fully typed, checked,
real type objects in Swift.
So the next thing I'd like to
show you, is not a new feature
and in fact, it's not
even an Xcode feature.
It's actually been part of
the standard Cocoa Text system
since before macOS 10.0, and
that's the find pasteboard.
Now you may not have
noticed before
that if you've done a find
perhaps by selecting some text,
like say Pause here
and then you copy it
to the clipboard
using Command C.
And then you bring down
the find bar with Command F
and then you paste it
in there with Command V.
That that same search, shows
up in other applications.
That's kind of weird isn't it?
Well, that's because the find
pasteboard is, it actually works
across applications just like
the clipboard that you all know.
across applications just like
the clipboard that you all know.
It lives in parallel
with the clipboard
and the two exist side by side.
So there's actually a really
cool trick that you can use
if you have something
that you want to hold
onto in the clipboard and you
don't want to blow it away.
But if you still want to search,
for something like start here,
you can just press Command
E, and it pushes it directly
into the find pasteboard.
And this allows you to then, hit
Command G, and cycle through all
of the remaining
hits in the file.
Now, if you just want to
quickly find and replace
in the exact same file, we
have a specialized command
for that too.
If you hold down
Command Control E,
you'll actually perform
an edit all in scope,
and this modifies every instance
of the symbol in that file.
So here I can just
add ED to this method,
because started sounds
a little bit better.
And, and this one's a
real big time saver,
And, and this one's a
real big time saver,
and I use this a lot.
Now, if all you want to do is
actually move some lines around,
you don't even need to use
the clipboard for that either.
We actually have a
dedicated command for that.
So if we just select
some lines here and hold
down Command Option
Bracket, you'll notice
that I can actually move this
entire chunk through in and out
of if functions and
other methods.
And they just flow
right through your code.
So this is all great
if you're just hanging
out in the same one file
at a time, but if you want
to do a find across other
files, for example if I'm going
to find all instances of timer,
you can do that by holding
Command F along with Shift
to do a full project find.
And here, I'll just push timer
into my find pasteboard
and search for that.
into my find pasteboard
and search for that.
And, this was actually
something that was pointed
out to me recently, I don't have
to click into the find navigator
to actually start to arrow
up and down these results,
Command G also works here too.
The only difference
is I just hold
down the Control key while
I'm hitting Command G
and it actually cycles through
all of the results in all
of my different files, including
my interface builder documents
and all of the instances
and hits in there.
And it works in reverse
by holding down Shift,
just like Command
G does as well.
So, I don't know, I know
some of you are actually kind
of writing some of this down.
But, you really don't have
to because there's actually
one place inside Xcode
that has the full list
of all of these commands
and all their keystrokes.
And that's inside of the
Xcode preferences window.
So here, in the Key
Bindings pref pane,
So here, in the Key
Bindings pref pane,
you can actually do a search
for anything that involves say,
find, and you can
see all the results.
Also, if you kind of vaguely
recall there may have been some
sort of like reveal command that
you, held down Command J for.
This actually searches across
the keyboard shortcuts as well.
So this is really powerful.
And if you don't like
our keyboard shortcuts,
for all of Xcodes
built-in commands,
you can actually set
your own from here.
And, if our built-in
commands are not enough and
or they don't do that
kind of just one thing
that you've always wanted
the source editor to do,
I'd like to invite Chris up now
to show you how you can add
your own commands, Chris.
[ Applause ]
>> Thank you, Mike.
As Mike said, I'm Chris
and today I'm going
to show you how you
can enhance Xcode
to show you how you
can enhance Xcode
with source editor extensions.
Now what we're offering you is
a way to add your own commands
to the source editor as
part of the Editor menu.
Your commands can
modify the user's text,
as well as selections
within that text,
say to perform navigation.
And unlike some other types
of application extensions,
you can implement any
number of commands
as a single Xcode
source editor extension.
Now we chose to base
Xcode extensions
on application extensions which
are the basis for extensibility
across all our operating
systems.
Since Xcode extensions are
application extensions,
each one runs in its own process
and can do pretty much whatever
it wants within its process
without interfering with Xcode
or with other extensions.
Of course, as application
extensions,
Xcode extensions are also
sandboxed and use entitlements
Xcode extensions are also
sandboxed and use entitlements
for doing anything that
needs to escape that sandbox.
And Xcode only gives access to
Xcode extensions to the text
and metadata that
they need at runtime
to perform their operations.
They don't get access to
the project structure,
they don't get access to
the user's files on disc.
And why are we doing
it this way?
Well, stability.
We want to ensure
Xcode is as stable
as possible for all
of our users.
Also, security, application
extensions are our way
of allowing you to enhance
both the operating system
and now our tools while
maintaining the integrity
of the entire system.
And of course, we're also
doing this for speed.
Application extensions are built
on top of Mach messaging and XPC
Application extensions are built
on top of Mach messaging and XPC
and are fully asynchronous.
So they can work as
quickly as possible
and not slow down our users.
And, there's another
reason too, that we decided
to use application extensions as
the basis for Xcode extensions,
and that's so you can distribute
them on the Mac App Store.
[ Applause ]
And just like all other
application extensions,
Xcode extensions are built
into a host application.
And this application
is a great place
to put your extensions
preferences
or configuration information.
For example to control which
commands a user actually wants
to make available
from your extension.
And it's also a great
place to put any other UI
that you want your
extension to provide.
Since all Xcode is going to
do is give you a menu item
for each of your commands.
And being part of an application
is what enables an application
And being part of an application
is what enables an application
extension, an Xcode extension
to be distributed on
the Mac App Store.
Of course, you can also
sign your application
and your Xcode extension
with your developer ID
and distribute it
however you want.
[ Applause ]
Now, let's talk a little bit
about how Xcode brings
your extensions to life.
To ensure the best possible
performance, Xcode is going
to find and automatically start
your extension pretty early
on in its own startup process.
Before your users
will need to use it.
And source editor extensions
aren't like some other kinds
of application extensions
that are only used
once, and then shutdown.
Xcode will actually try to keep
your extension alive as long
as possible so that it
can send as many commands
as possible so that it
can send as many commands
as the user wants to invoke.
Now when your extension
is started,
if it needs to do any
work, at that startup,
Xcode will send
extensionDidFinishLaunching
to it.
And this is a good
place to do startup work
as long as it's quick.
That's right you need to do your
startup work as fast as possible
and that's so your users
can get to your extension
as soon as they want to.
And to help you there,
Xcode ensures that starting
up your extension is
asynchronous with starting
up other extensions and
with its own startup.
Now once your extension is
started, Xcode will ask it
for its commands, and
its commands can come
from one of two places.
By default you'll have an entry
in your NSExtensionAttributes
dictionary in your info.plist
in your NSExtensionAttributes
dictionary in your info.plist
that specifies all of the
commands in your extension.
But you can also
provide an override
of the commandDefinitions
property
from your extension class
that overrides the values
that are given back
by the info.plist.
So if your extension has a
dynamic list of commands,
say because it's downloaded
some new JavaScript
that it actually uses
to run those commands,
it can provide a new collection.
Now once Xcode has
your commands,
it'll give each extension its
own submenu of the Editor menu
when the user is
editing source code.
And the extensions are listed
in alphabetical order just
as they are in the
finder so that they're all
in a stable place for the
user, from run to run of Xcode.
However, because order of
commands is often important
and often, conveys a
bunch of semantics,
Xcode will preserve the order
that you give your commands
Xcode will preserve the order
that you give your commands
to it, and list them in
the menu in that order.
Now to invoke a command,
of course a user can choose the
command from your menu item,
or they can press a
keyboard equivalent
that they have assigned.
Your command object
will be instantiated
and sent an invocation
and a callback.
Now the invocation packages up
all of the data and metadata
that your command
needs to do its work.
And, the command then uses the
callback once it's done its work
to tell Xcode that it's done.
And let's take a look
at the actual APIs.
Here we have the
simple protocol that all
of your command classes
need to conform to.
Just as I said, it's
passed an invocation
and a completionHandler
call back.
and a completionHandler
call back.
And that invocation just carries
a few simple pieces of data.
It has a commandIdentifier which
is also set in your info.plist
or in you command
definitions array.
And that lets you
distinguish multiple commands
that are handled by
the same command class.
After all, there are a
lot of commands especially
in source code editing that do
just slightly different things.
So you'll probably
want to implement a lot
of things using the
same command class
and just handle a couple
different special cases
in each one.
And this identifier lets you
figure out which of the commands
that the user has invoked.
We also provide a
property that you can set,
a cancellationHandler block on.
And this cancellationHandler
is called
if the user cancels
your command.
And that can happen if your
command is taking too long
and we'll get a little bit
into that a little further
on in the presentation.
And finally, of course,
the invocation also
contains the buffer
of text the user
is working with.
And that SourceText is
represented by an instance
of another object,
XCSourceTextBuffer.
And it has a bunch of metadata
in addition to the text itself.
We give you the uniform
type identifier
that Xcode thinks the file
containing that text conforms to
and that way you know if you're
working with Swift source code,
XML data, and ObjC++, header
file, whichever form of text.
We also provide the
indentation settings
that Xcode has for that file.
So that as you're making
changes to the text in the file,
you can also conform to what
the user expects Xcode to do
when it's indenting that file.
Now because tabWidth,
indentationWidth,
and whether
to useTabsForIndentation have
some subtle interactions.
We've also provided
a bunch of details
We've also provided
a bunch of details
about how they work
together in our header files.
So I encourage you to check
out that header documentation,
see exactly how those
all fit together.
Now, we provide the text
that the user is working
with in two different ways.
If you need to process the
text as a single stream,
say by piping it to a command
line tool, you'll probably want
to work with the completeBuffer,
which represents the text
the user is working with,
the entire file as
a single string.
However, if you only need to
make minor changes to the text,
this is a really
inefficient way to work
since your extension
needs to send
that whole buffer back to Xcode.
And that's we also provide
this mutable array property
containing the lines
of text in the file.
We found ourselves, that
when writing tools that work
We found ourselves, that
when writing tools that work
with source code, we
actually find a line
and column abstraction works
much better than just working
with a single huge
buffer of text.
And this, by being
a mutable array,
also lets Xcode actually
track what you're changing
so that we only need to send
back the individual changes
you've made, we don't need to
send back the whole buffer.
And this can really
help improve performance
for editing extensions.
And in addition to providing
the lines of text in the file,
we also provide a mutable array
for the selections in the file.
There will always be
at least one selection
which is either the user's
insertion point or selection
and because Xcode's
editor is built on top
of the Cocoa text system,
we support multiple
selections as well.
And if you want to change
the selection all you have
to do is change this
mutable array.
to do is change this
mutable array.
And what's in that array are
source text range objects.
Now we use SourceTextRange
instead of NSRange
because we really do
believe that the line
and column abstraction that
we provide is the best way
to do most text editing.
And that's why a source range
instead of being a position
and a length, is actually
a start and end position
and those positions
are represented
in terms of line and column.
Now, I'm going to
show you a demo
of how you can create
your own Xcode extension.
Let's go to the demo machine.
And, let me get my
notes set here.
So I was really impressed
by what Mike told us
about Swift literals, the
Swift color and image literals.
about Swift literals, the
Swift color and image literals.
And I'd really like to
use them in my own code.
So I think what I'm going
to do is create a new
source editor extension
that can automatically convert
any uses of UIImage or UIColor
to the corresponding
Swift literals.
I'm just going to create a new
Xcode project, and I'm going
to create a new OS
X application.
After all, my app
extension, my Xcode extension,
has to be carried
inside an application.
So I'll create this
and I'm just going
to call this Chris Literals.
And I'm just going to give
myself an organization
identifier of com.example, and
press Next, then I'm just going
to put this project
on the Desktop.
And now that I've
created my project,
I'm going to add a new
target to it and I'm going
to add an OS X application
extension target.
to add an OS X application
extension target.
And you can see we have our new
Xcode Source Editor Extension
template right here at
the end of the list.
And I'm going to name
this, Chris Convert
to Literals and just finish.
And when I clicked Finish, Xcode
offered to activate the target
for me, just as with
any other app extension.
So I'm going to accept that.
And now let's take
a look at the code
that Xcode generated for me.
In this convert to literals
group, Xcode added a class
that represents my
extension itself.
And this just conforms
to the XCSourceEditor
extension protocol.
And it also added templates
for a couple of, for a method
and a property that I can
uncomment if I really want
to override these and
provide my own implementation.
I don't want to right now,
I don't think I need it
for this particular project,
so let's just take a
look at the next file.
In the next file
SourceEditorCommand,
I have my first command class.
And just as I showed
you it conforms
to XCSourceEditor command,
and has just a single perform
within vocation method.
Now let's take a
look at my info.plist
that Xcode created for me.
And you can see, in my extension
attributes, Xcode added a set
of
XCSourceEditorCommandDefinitions
which is an array
of dictionaries.
And that dictionary specifies
the class to instantiate
for a command, the identifier
to use for that command,
and the name that the menu item
for that command should take.
So I'm going to change
that to Convert
to Swift Literals
and just accept that.
to Swift Literals
and just accept that.
Zoom out, and go back
to our command itself.
Now I'm going to cheat
a little bit here,
because I've already written
this code and I'm just going
to use a code snippet it
to insert what I've
already written.
I've named it Chris
Demo, and you can see
that it's actually not
all that much code.
So what I'm doing is just
looping through all of the lines
in the file, and if the
line actually has a UIColor
or a UIImage invocation in
it, I'm just replacing it
with its corresponding
Swift literal syntax.
And then, if the line
has actually changed,
that's when I replace the
line in the lines array,
that way I'm not replacing
every line in the array,
and I'm certainly not
modifying the complete buffer.
and I'm certainly not
modifying the complete buffer.
I'm only modifying the lines
that actually need to be.
And I'm also keeping track
of the lines that I modify,
so that later I can also
construct a set of selections
to represent those lines.
I set those selections,
and then I just invoke my
completionHandler to let Xcode
know that my command is done.
I see Xcode in the
suggested applications.
And if I click Run here, what
do you think is going to happen,
beyond of course
building my extension.
Well you can see we launch
another instance of Xcode
for you to test your
extension against.
And we actually provide
a little bit
of visual distinction here too,
we darken the icon in the dock
and we darken the icon in
the, welcome to Xcode window,
so you know at a glance
that this Xcode is one
so you know at a glance
that this Xcode is one
in which you're testing
an extension.
Now I'm just going to open
up Mike's jogger project
because I noticed he had some,
uses of UIImage and UIColor
that weren't converted yet.
And you can see another case
where we've modified the UI
in the activity view up at the
top to specifically indicate,
at a glance, that this Xcode is
being used to test an extension.
And Mike's left me actually
looking at some cases of UIImage
and UIColor that I think
would look nicer as literals.
So I'm just going to
look in the Editor menu,
I see my Chris Convert
to Literals extension,
and I see my convert to
Swift literals command.
Now if I go back to
my original Xcode,
and just set a breakpoint,
say down here.
If I actually run
my command now,
you'll see that nothing happened
and that's because I'm stopped
at that breakpoint in the
first Xcode's debugger.
So you can actually bring all
of the power of Xcode's debugger
and LLDB to bear in debugging
your extensions as well.
I'm just going to
continue from that though.
And I've still got my breakpoint
in there, let's get rid of that.
And if I go back, I can see
the Xcode highlighted all
of the lines that I've
changed, just as I told it to,
and it's converted everything
to using the new
Swift literal format.
[ Applause ]
Now let's say I actually
want to do this
on a pretty regular basis.
Well it's easy enough, I
can make that pretty fast
by just adding a key
binding for my new command.
I do that in Xcode's
preferences in the Key Bindings,
and I'm going to search
on the name of my command
which I know starts with Chris.
And we're right there
in Xcode's Key Bindings,
I think I'm just going to name
this, command option control/,
that's easy to remember
right [laughter]?
Now let's go back to the slides,
and talk about something
that's a little near and dear
to my heart when working
with Xcode extensions,
and that's speed.
Text editing is a user
synchronous activity.
Your users are really going
to want to keep their hands
on the keyboard and just
type as a continuous stream,
even while they're
invoking your extensions.
And your extensions really
shouldn't prevent them
from just continuing
on with their typing.
Now in order to prevent
any race conditions
between your extension and
the user, Xcode actually locks
between your extension and
the user, Xcode actually locks
out changes to the document
that the user is working
with when they invoke
an extension.
And fortunately, this means
you don't have to worry
about reconciling the
changes you've made
and the changes the user made.
On the other hand, it means
that if your extension takes
too long, which we define
as a couple of seconds,
the user's locked
out from editing their document.
So, what do we do there?
Well we give the user the
opportunity to cancel.
And we do that by bringing
down a cancellation banner.
And I think the system's
trying to tell me
that this slide is taking
a little too long so,
let's cancel out
of it and go on.
Now Xcode helps keep
things fast for our users
by starting extensions early
and keeping them alive as long
by starting extensions early
and keeping them alive as long
as they're likely to be
needed so it can send commands
as soon as they're invoked.
And as we talked about earlier,
using the lines array ensures
that your extensions data
transfer is also optimized
for performance.
And when a user needs
to cancel a command,
that cancellation is actually
immediate on the Xcode side.
As soon as the user hits Cancel
they can continue typing.
Now your extension will still
receive that cancellation
and still needs to react to it,
but the user doesn't
need to care.
Of course there are a few ways
your extension can help Xcode
with performance as well.
Your extension can startup
as quickly as it possibly can
so it's ready as
soon as the user is.
In implementing your commands
you can use GCD in all
of our asynchronous
programming patterns
to ensure you're making
maximum possible use
to ensure you're making
maximum possible use
of the user's system and
getting back to Xcode
as quickly as possible.
And of course, you can avoid
replacing the whole buffer
of text if you don't have to.
And finally, as I said, you need
to handle cancellation quickly.
Because until your command
finishes handling its
cancellation, it won't be
available to the user again.
Now today Mike showed you a
bunch of great new features
in the Xcode source
editor, like our ability
to add documentation comments
and our support for Swift color
and image literals
in code completion.
And he also showed
off some features
that were very recently added
but are already shipping
in Xcode 7.3, like
fuzzy code completion.
And I showed you how Xcode
source editor extensions work
and how you can make them
yourselves, I can't wait
to see what you do
with our new API.
to see what you do
with our new API.
There'll be some more
information available
at our page on the
WWDC 16 website.
And there are a couple
of related sessions
that you can check out on video,
in particular Optimizing App
Startup Time doesn't just apply
to apps, it also
applies to app extensions
because they use a lot
of the same technologies.
Our Introduction to Xcode is
also great for people coming
up to speed on the
Xcode environment
and what your users will expect
when you implement extensions.
And finally, we have
some sessions
from previous years
talking about what it means
to be an app extension and best
practices in implementing them.
And we'll also be at the bash
tonight, so, thanks for coming
to WWDC and if you
have any questions
for us, you can see us there.
[ Applause ]