Writing a Sketch Plugin to easily generate iOS Assets

Before we begin with the code, I wanted to point out that in just a couple of months I have completely replaced Photoshop with Bohemian Coding’s Sketch. This software has clearly been developed with user interface workflows in mind and it’s exactly what I need to design my UIs. Furthermore, we can easily create plugins […]

Source: Writing a Sketch Plugin to easily generate iOS Assets

 

sketch

WRITING A SKETCH PLUGIN TO EASILY GENERATE IOS ASSETS

Before we begin with the code, I wanted to point out that in just a couple of months I have completely replaced Photoshop with Bohemian Coding’s Sketch. This software has clearly been developed with user interface workflows in mind and it’s exactly what I need to design my UIs. Furthermore, we can easily create plugins that extend its functionalities. What more could I need?

Let’s get back to work.

In this article I want to show you how to create a plugin that helps you generate iOS assets with @1x, @2x and @3x scale factor. Without further ado let’s start writing this useful plugin (here you can download the Sketch trial version if you don’t have it already).

SKETCHPLUGIN LANGUAGE BASICS

The Sketch script language is written on top of CocoaScript that acts essentially as bridge between Javascript and Objective-C. You can decide which style better suits you: dot or bracket notation. For example you can write:

1
2
3
4
// Dot
var l = selection.length()
// Bracket
var l = [selection length]

There are a couple of predefined properties that are used to access important elements of the script workflow that are worth mentioning :

doc: A reference to the current document.
It comes in handy when you want to perform operations related to the whole project, like setting the current zoom level:

1
[doc setZoomValue:3.0]

selection: an array which contains all the layers currently selected by the user.

With this code, for instance, we change the name of the selected layers:

1
2
3
for (var i = 0; i < selection.length(); i++){
    selection[i].name = "Selection "+i
}

Another important class that you are going to deal with is MSLayer: this is the base class for every layer in your artboards. You can access a layer’s frame to get its size and position; you can change layer name and get its children, or select/deselect it usingsetIsSelected.

GET THE DOCUMENTATION

You can find more information about Sketch classes here.
The documentation is not complete though, so you have to peek at the headers of the application to find a specific method or to guess how a class works.

To enter this information you need to perform a dump of the Sketch App Mach-O file. Fear not, this operation is extremely simple thanks to class-dump. Download the latest version and follow the next steps.

1) locate your Sketch app that should be located into the Applications folder.
2) Right click on the App and select “Show Package Contents”
3) Explode folder “Contents” and then “MacOS”
4) you should see the Sketch executable

Now open the terminal. We are going to launch the class-dump script, but first create an empty folder on your desktop and call it SketchHeaders

Now in your Terminal prompt write

1
./<PATH TO CLASS-DUMP>/class-dump -H <PATH TO SKETCH EXECUTABLE> -o <PATH TO SketchHeaders FOLDER>

To easily obtain the required paths you can just drag and drop the executable and the empty folder directly into the terminal right after the -H and the -o flags.

Launch it and a lot of .h files will appear in your SketchHeaders folder.

DEBUG

The last thing to manage before starting with the Plugin code is a way to reach logs from Sketch.

In your scripts you can log output using the function log

1
log(“ciao!”)

Logs and any other output from Sketch will be displayed in the system.log file. Use this command from the Terminal to see an auto-updated version of the last rows of that file:

1
tail -f /var/log/system.log | grep Sketch

BUILDING EXPORTABLER

What does this plugin do exactly? Well, open Sketch and draw a simple rectangle. On the bottom right panel you should see a button named “Make Exportable”. If you click that button you see that a default Export Option has been added:

The option describes the size, a suffix and the format of the export asset, when you click on the “Export Rectangle” button you get the asset sliced exactly following that information.
When you produce assets for iOS you end up creating regular, @2x and @3x versions of your UI elements. To achieve this result in Sketch you can add the three Export Options that reflect these assets. It turns out that doing this work for a lot of UI elements is a tedious process, so we can automate this procedure writing a plugin. ;)

LET’S CODE

Download the final version of Exportabler (yes, it’s a terrible name!). From Sketch menu go to “Plugins->Reveal Plugin Folder” and paste Exportabler folder in the plugins folder. You should see “Exportabler” as new voice under the Sketch Plugin menu with two sub-items: “1.Add export options” and “2.Remove export options”.

As you can see this structure reflects exactly the hierarchy of the file system into the Plugins Folder. I’ve named the files exactly as I want them to appear within the Plugin menu.

Another interesting thing is the way we can add shortcuts to launch the plugin functions. Open file “1.Add export options.sketchplugin” and check the first row.

1
// (shift cmd a)

What you write inside the parentheses in the first row of the file is the shortcut assigned to execute its content.

Every time you launch a plugin, the whole content of a .sketchplugin file is executed, so you should split plugin functionalities in different files.

You can also import a js file inside your plugin code, as we did for the “Add export options”:

1
#import 'ExportablerConfig.js'

This file contains just a simple configuration that describes the scale used in your interfaces (most of the time it is @2x). We’ll use this value later.

1
2
3
4
5
Config = {
    
    // The scale adopted to design the UI
    scale: 2
}

The next part of the script takes care of checking if the main plugin function should be executed:

1
2
3
4
5
6
7
8
9
10
11
12
if (selection.length() > 0){
    
    for (var i=0; i < selection.length(); i++){
        addExportSizes(selection[i])   
    }
    [doc showMessage:"Export Options Added"]
}else{
    
    var app = [NSApplication sharedApplication];
    [app displayDialog:"Anything selected <img src="http://www.thinkandbuild.it/wp-includes/images/smilies/icon_sad.gif" alt=":(" class="wp-smiley"> " withTitle:"Exportabler"];
}

If the selection array is greater than zero we can execute the main functionaddExportSizes on each selection and use the [doc showMessage:] function to notify the user that the operation is complete. Otherwise we display a default dialog to report that there are no active selections.

Now let’s check the core of this plugin, the addExportSizes function.

At the very top of the function we define the three Export Options:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var iOSExportData = [
  {
    "format": "png",
    "scale": 1/Config.scale,
    "suffix": ""
  },
  {
    "format": "png",
    "scale": 2/Config.scale,
    "suffix": "@2x"
  },
  {            
    "format": "png",
    "scale": 3/Config.scale,
    "suffix": "@3x"
  }
]

Note how the scale parameter is calculated: depending on the initial resolution adopted by the designer we define how much the relative assets need to be scaled. For example if the design is initially scaled at 2x, to obtain the @2x assets the scale factor of the slices is 1, so no scaling at all.

As I’ve previously shown, from the Sketch UI you can add Export Options defining different size/suffix/file type information.
Now we programmatically remove any Export Sizes.

1
2
3
4
5
var export_options = [layer exportOptions]
var export_sizes = [export_options sizes]
while([export_sizes count] > 0) {
        [[export_sizes firstObject] remove]
}

and we add the new Export Sizes contained in iOSExportData defining format, scale and name (suffix).
The code is self explanatory:

1
2
3
4
5
6
7
for (var i = 0; i < iOSExportData.length; i++) {
  var size_data = iOSExportData[i]
  var size = [[layer exportOptions] addExportSize]
  [size setFormat:size_data.format]
  [size setScale:size_data.scale]
  [size setName:size_data.suffix]
}

At this point, to see the new Export Sizes in the Inspector we have to use a trick, so we deselect and select the last Layer again.

1
2
[[doc currentPage] deselectAllLayers]
[layer select:true byExpandingSelection:true]

You can try to select a Layer, executing the Plugin and you should see the three Export Size options appearing on the inspector.

This is a really simple plugin but it can drastically improve your everyday workflow. Imagine what you can build with just a few lines of code and a clear goal in mind! Let’s talk about your next plugin on Twitter and poke me if you have any question or suggestion.

Cheers!