During this year’s WWDC, Apple announced Xcode Source Editor Extensions, a new and easy way to broaden the capabilities of our favorite tool. If you’re curious as to how you can harness its power to make you more productive, keep on reading.
Many bright minds in the software development industry believe that our profession is a form of craftsmanship. One aspect of this metaphor is especially true — tools. A great craftsman requires great tools but as the proverb goes “a bad workman always blames his tools”.
Christoph Gockel reminds us that we should “pick the right tools for the job” and if there aren’t any, we should create our own, so that is exactly what we are going to do.
Xcode used to have an abundance of third party plugins. “Used to”, because Xcode 8 will no longer support them. This is very sad news but they had especially good reasons (in relation to security and reliability) to do so.
Apple also knows that engineers don’t like to vacuum and to prevent us from doing something irresponsible like stripping IDE of code signature, they are giving us a new way to build our own custom tools. The WWWC talk gives a great description of new possibilities, but at App’n’roll we know that the best way to learn is by getting our hands dirty.
Many of you probably work with networking in your apps and from time to time you have to parse JSON and write model for it. This is a task that IDE could do for us, so we decided to write an extension for it. To keep things simple we had to make some assumptions. First, we expect that currently edited file’s content must be a JSON. Second, we will ignore edge cases related to nested objects.
Important notes: Our example was created with Xcode 8.0 beta 2, so it may not always work as expected. If you are still working on El Capitan you have to follow instructions from Xcode 8.0 beta Release Notes - Known Issues in Xcode 8.0 beta — IDE, Source Editor Extensions. Also remember to be patient when running your extension. If you open project too fast — before extension will load — it will not be available from the menu of test Xcode’s instance.
Let’s start by creating a new macOS project (be sure to check the Unit Tests box) and adding new target to the default app — an Xcode Source Editor Extension:
This will create an Info.plist for us and two new classes. Before digging into the code let’s look into the Plist first. To change the names displayed in Xcode’s menu you have to edit Bundle Name and XCSourceEditorCommandName.
First of the classes generated for us confirms to XCSourceEditorExtension and will notify us when the extensions will be loaded. We will not modify it in our project. The second class confirms to XCSourceEditorCommand — a protocol with only a one method — perform (with invocation:, completionHandler:) that will be executed when we call the command. The extension will provide us with the content of the current file and ways to modify it. We will use a simple layer of abstraction to make unit testing easier. Our interaction with the file with be restricted by SourceFile protocol:
Our next step is creating a helper object that will be easy to test.
Let’s call it JSONConverter:
It has just one method that accepts SourceFile and may throw an exception if something will go wrong. Integration with XCSourceEditorCommand requires a simple adapter and looks like this:
Running extension and executing the command does nothing — as we expected. Now is the time for some TDD. First we need to setup system under test and write a test double for the source file:
Our first test will check for parsing invalid JSON:
And this is the code that will make it pass:
The next step is parsing a simple JSON with one String property.
Here is the test:
…and here is the implementation:
Now we’re going to cheat a little bit — we’re going to write three tests for different variants of NSNumber:
…and we’re going to make them pass all at once. This code checks for runtime type of the object and uses corresponding Swift type for it:
Next on our list is an array. Here is test:
The last feature that we want to support is parsing a nested type. The simplest possible test is enough to illustrate this:
Nested type requires more changes than other types so here is complete implementation of the JSONConverter after changes:
That’s it. We need to update some of our tests to make them pass after changes in model naming system. JSONConverter is already integrated with XCSourceEditorCommand so the last thing that we have to do is check how it works in Xcode.
Congratulations you have just finished your first Xcode Source Editor Extension.
Tooling is one of the most important areas for every software developer. Our IDE providers don’t always give us what we need so sometimes we have to take matters into our own hands. Xcode Extensions are a great starting place to learn how to do this.
There are not much possibilities at the moment, but knowing Apple this is just a first step and when we will get access to AST and file system of the project — sky is the limit. You can find full source code for the example on Github.
We’d Love to Hear from You
Did it work for you? Have we broadened your understanding of Xcode Extensions? Would you like to see more content like this? Is there anything you’d like to add, have we missed anything?
Please feel free to share your experiences and questions with us in the comments below or via social media (send us some photos or videos too), you can find us on Facebook, Twitter, Instagram and Pinterest, let’s connect!
All images used are CC0 1.0 Universal (CC0 1.0).