Design+Code logo

Quick links

Suggested search

illustration

Sketch Plugin Project

icon

Add to favorites

Sketch Plugin Environment and the Package Manager

play icon

Play video

cancel

Downloads for this Section

At the end of this section, make sure to download the finished project to compare your results.

Sketch Package Manager

To build a Sketch plugin, you may either comply with the directory structure and necessary files required to be interpreted by Sketch or use the SKPM (Sketch Package Manager) wizard-like command-line interface.

In this tutorial, we are going to rely heavily on the SKPM. This tool is distributed primarily through Node and its NPM (Node Package Manager). We strongly suggest you install it before going any further. After you get NPM on your machine, install SKPM by calling npm i -g skpm on the command line.

Creating the Project

You can use package manager to create a project. On a select folder for the project, run skpm create Angle or whichever name of plugin you might want. After creating a directory and fetching dependencies, the SKPM is going to prompt you to cd Angle to navigate downwards to the folder and begin development.

Other options will be presented to run, build and publish the plugin project. Don’t worry for now, by the end of this series you will use all of them.

Configuring the Development Environment

In the development videos, Visual Studio Code is being used as the development environment. Drag the plugin project folder into its icon to create a new window with the navigator set in the correct directory.

As much of your development is not going to happen on the interface, let’s toggle the integrated terminal using control+` or selecting View > Integrated Terminal on the menubar.

When running plugins, Sketch tries to be efficient by loading the plugin resources at once and avoids reloading them for performance. As this is not useful in the development environment, force it to reload the plugin scripts by running on the terminal:

defaults write ~/Library/Preferences/com.bohemiancoding.sketch3.plist AlwaysReloadScript -bool YES

With this enabled, every time you hit save on a script of your plugin, it will reload and fire on Sketch.

Running the Plugin

Every time you are developing a Sketch Plugin, you will be required to navigate to the project directory on the terminal and toggle the continuous regeneration by running npm run start.

This last command will build your project, start Sketch and immediately try to run the single line of code present in the default function of src/my-command.js which would be:

export default function(context) {
    context.document.showMessage("It's alive 🙌");
}

Try to change the text given to the show message method and see it appearing on the screen.

Logging on the Console

Some of the development will not be UI related, so it is good to have a way to output data to the terminal. On VSCode, you can trigger a new tap on the Integrated Terminal using the + button. SKPM offers a handy command to listen to all logs done inside a running extension:

skpm log -f

Wherever you are in your scripts you may call log with an argument to output data. print("This works just like Swift's print");

Plugin Context

The plugin’s default function has a context in its signature. This variable is the current context in which the plugin was called, and it is a JavaScript Object that contains references to the JavaScript API, the current command, the active document, the plugin, the script path, the script URL and the current selection. Let’s use print on it to review its values:

print(context);

You will see that this dictionary returns a few types that are opaque to JavaScript such as MOJavaScriptObject, MSPluginCommand, MSDocument, MSPluginBundle. These object classes are bridged from Objective-C and they will require some taking care of to be worked with. More on that in the following sections.

Deconstructing the Context

It is JavaScript lingo to give descriptive function arguments using an options dictionary and deconstruct them on the other side. By placing a dictionary signature on the default function argument, the arguments will be automatically assigned from the dictionary.

export default function({
  api,
  command,
  document,
  plugin,
  scriptPath,
  scriptURL,
  selection}) {
  // Function definition
}

Selection

The first thing we should take care of is the selection. Angle is a plugin that gets a selection of a polygon or mockup and offers the user options of artboards to apply. You can see that most of this logic does not deal with all the complication of a perspective transform so let’s take care of getting those selections.

Using the R command in Sketch, draw two rectangles. Then, go back to the editor and write:

print(selection);
// This will show on the console
(
    "<MSShapeGroup: 0x7fa1206f9ac0> Rectangle (C5AE2710-C237-4D25-81DE-2D9ED5751E11)",
    "<MSShapeGroup: 0x7fa1204d9050> Rectangle Copy (F78DDA3C-0864-41CC-96F0-9AF71A15CAAC)"
)

Dealing with NSArray

On the console, you will receive an array of shapes you have just created. This array very ordinary because it can be accessed via indexes by using subscript notation such as: array[1]. Nonetheless, if you try to use the length variable, it will fail because this is not a JavaScript Array but an Objective-C NSArray. To get the length of an NSArray, you need to use the count method.

When writing JavaScript we will try to keep as far away as possible to using Objective-C constructs. In that thought, lets extend the JavaScript Array functionality by creating a function that translates NSArrays:

Array.fromNSArray = function (nsArray) {
    let array = [];
    for (var i = 0; i < nsArray.count(); i++) {
      array.push(nsArray[i])
    }
    return array
}

Then, on the default function, write:

let selectedLayers = Array
    .fromNSArray(selection)
print("🔷");
print(selectedLayers.length);

This newly created JavaScript Array gives us access to any Higher-Order Functions such as map, reduce, filter. These functions will come in handy when dealing with multiple selections and artboards and will be covered in a later section.

Getting Artboard from Page

The artboards to show on the dialog are the ones from the current document available on the context dictionary. After accessing them, we are going to transform them into a JavaScript Array for better handling.

let artboardsOnSelectPage = Array
    .fromNSArray(document.artboards());
print("🖼");
print(artboardsOnSelectPage.length);

Notice that by using let in JavaScript you are ensuring that this is going to be the only variable with this name in the function scope. Different from Swift, this does not ensure value immutability.

Artboards from Other Pages

These last artboard we have listed are from the current page. To get artboards from other pages, we need to sequence the pages, filter the current page and include inside an array the artboards for each page.

let artboardsOnOtherPages = [];
let pages = Array.fromNSArray(document.pages());
pages = pages.filter(page => page != document.currentPage());
for (var i = 0; i < pages.length; i++) {
    var artboards = Array.fromNSArray(pages[i].artboards());
    artboardsOnOtherPages = artboardsOnOtherPages.concat(artboards);
}

The result will be a JavaScript Array of all the artboards in the document that are not on the select page. By keeping those separate, we can list the artboards on the select page before those on other pages on the dialog.

Configuring the Dialog

After getting the list of selections and all the artboards on the document, the next step would be to present the alert for the user to configure those options. Let’s create a function with which we are going to pass those variables:

function getSelectionAndOptions_forAngleInstances({ scriptPath }) {}

At this moment, we are going to leave this at showing the count of select objects and document artboards by creating an NSAlert and configuring its message.

let alert = NSAlert.alloc().init();
alert.setMessageText("Apply Mockup");
alert.setInformativeText("Choose an Artboard to apply into the selected shape.");
alert.addButtonWithTitle("Apply");
alert.addButtonWithTitle("Cancel");

When doing native interfaces, you have access to Cocoa classes such as NSAlert. They offer an Objective-C interface to JavaScript, such as the one we have just used.

return alert.runModal();

Placing an Icon on the Dialog

By default, an alert is going to inherit its icon from the application it is being called from, in this case, Sketch. On Angle, we are setting our own icon on the alert. Let’s create a load local image function to customize the alert.

function loadLocalImage ({ scriptPath, filePath }) {}

The icon required by NSAlert is an NSImage, and to get it from a bundle you will need to add the image file into the Context/Resources folder of your project so it can be accessed by the path of the script as follows.

let basePath = scriptPath
    .stringByDeletingLastPathComponent()
    .stringByDeletingLastPathComponent()
    .stringByDeletingLastPathComponent();  
return NSImage.alloc().initWithContentsOfFile(basePath + "/" + filePath);

Angle Logo

Before running modal on the alert, it is appropriate to call customize it with the icon with the dictionary as follows:

alert.icon = loadLocalImage({
    scriptPath: scriptPath,
    filePath: "Contents/Resources/logo.png"
});

Download the image for the Angle Logo and place it on your project inside the .sketchplugin bundle under Contents/Resources. In VSCode, you may disclose the bundle using the triangles. On Finder, you may right-click on the bundle and click the Show Package Contents.

logo

Showing the Alert

It is JavaScript lingo to give descriptive function arguments using an options dictionary. As the matching pair of the object deconstructions, let’s write an options object as the argument of the function at the last line of the default function call:

getSelectionAndOptions_forAngleInstances({ scriptPath: scriptPath });

showing-the-alert

Conclusion

In this first section, we learned that Sketch exposes all it’s API to JavaScript and that it is possible to build native macOS UI using it. Nonetheless, we will have to be cautious when bridging from and to Objective-C. We have also set the environment and gathered the selection and the artboards on the file.

Next, we will define an Angle class and use it to grow the project and share code between the many ways to build a mockup.

READ NEXT

Angle Class

Templates and source code

Download source files

Download the videos and assets to refer and learn offline without interuption.

check

Design template

check

Source code for all sections

check

Video files, ePub and subtitles

Assets

Videos

ePub

Subtitles