Learn about all of the new, and a few existing, features of Xcode source editor. See how you can make the most of new code completion features, Swift image and color literals, and snippets. We'll also show how to add commands to the source editor with new Xcode Extensions that you can distribute on the Mac App Store.
[ Music ]
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 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
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.
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,
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
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
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.
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.
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.
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.
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 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.
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,
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.
Thank you, Mike.
As Mike said, I'm Chris and today I'm going
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
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?
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
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.
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
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.
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 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
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
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,
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
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 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,
to useTabsForIndentation have some subtle interactions.
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
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.
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.
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.
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
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.
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.
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
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.
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
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
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
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
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.
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.
Looking for something specific? Enter a topic above and jump straight to the good stuff.
An error occurred when submitting your query. Please check your Internet connection and try again.