Sketch is a popular UI design software in recent years. Compared with Illustrator or Photoshop, it is small and simple but has enough functions. Meanwhile, it is more friendly to Mac touch pad support. It also has a much friendlier plug-in system than Adobe, with lots of plug-ins to help solve synergy and efficiency issues.

The great benefit of the Sketch plug-in is that it can be developed directly in JavaScript and comes with a number of supporting development tools. Here, I will take the Placeholder plug-in, which helps designers quickly insert a Placeholder map, as an example to take you step by step to understand how to develop Sketch plug-in.

Before proceeding to plug-in development, we need to understand some basics. Sketch is a set of native Objective-C software. It supports JS development because it uses CocoaScript as the plug-in development language. It’s like a Bridge that allows us to write OC and JS in our plugins, and then Sketch encapsulates the basic methods and implements a set of JavaScript apis so that we can develop our Plugins using JS.

Note: Create a Plugin is an introductory tutorial on how to develop plug-ins. You can also read this tutorial for 2-3 minutes before reading the rest of the article.

Need to sort out

Before we start plug-in development, let’s go through the features we need to implement. We will use placeimg.com/, a site dedicated to generating placeholder maps, to create a feature that generates a placeholder map of a specified size and inserts it into the Sketch palette. The plugin provides a panel that allows the user to enter options such as sizes and categories, as well as an Insert button that inserts an image layer onto the artboard.

Initialize the project using SKPM

SKPM is the official plugin management tool for Sketch, which is similar to NPM in Node.js. It integrates the creation, development, construction, and publishing of plug-ins into one, and we need to use it in many scenarios. Installation is relatively simple, directly use NPM global installation can be.

npm install -g skpm
Copy the code

Following the official tutorial, after installation we can use the SKPM create command to initialize the project directory. Of course SKPM supports template-based initialization. The official repository also lists some templates. We can use –temlate to specify the template for initialization. But for educational purposes, I’ll use the official default template here.

➜ ~ SKPM create sketch-placeimg stocking Done! To get started,cd into the new directory:
  cd sketch-placeimg

To start a development live-reload build:
  npm run start

To build the plugin:
  npm run build

To publish the plugin:
  skpm publish
Copy the code

SKPM is packaged and compiled internally using Webpack, and running NPM Run build produces the sketch-placeimg.sketchplugin directory, which is the final plugin directory. Double-click the directory, or drag it onto the Sketch screen to install the plug-in. Similar to WebPack — Watch, running NPM Run Watch allows you to listen for file changes and compile them in real time, which is very helpful in development.

Note: Do not use NPM start for development, it carries the –run command, which can make builds very slow. While it would be handy to have Live Reload, it’s not recommended until the issue is officially fixed.

Introduction to Project Structure

The template directory structure is as follows. To help you understand, let’s briefly introduce these directories and files.

.Bass Exercises ── sketch-assets ├── ├─.bass Exercises ── sketch-exercises Contents │ ├ ─ ─ Resources │ │ └ ─ ─ icon. The PNG │ └ ─ ─ the Sketch │ ├ ─ ─ the manifest. Json │ ├ ─ ─ my -command. Js │ └ ─ ─ my -command. Js. The map ├── ├─ SRC ├─ manifest.json ├─ my-commandCopy the code

package.json

As with most JS projects, SKPM creates projects with package.json files. In addition to recording the dependencies and shortcut commands for the project as before, the file also adds the SKPM field to configure the SKPM. The default value is as follows.

{..."skpm": {
    "name": "sketch-placeimg"."manifest": "src/manifest.json"."main": "sketch-placeimg.sketchplugin"."assets": [
      "assets/**/*"]."sketch-assets-file": "sketch-assets/icons.sketch"},... }Copy the code

The name of the plug-in is specified as Sketch -placeimg and the manifest file of the plug-in is SRC /manifest.json. Main represents the name of the plug-in directory that was eventually generated. Assets represents the images and other related assets that the plug-in depends on. During compilation, the files matching the configuration will be copied to


/Contents/Resources.

manifest.json

Manifest. json is the package.json file of the Sketch plugin. Let’s take a look at the manifest.json generated by default.

{
  "$schema": "https://raw.githubusercontent.com/sketch-hq/SketchAPI/develop/docs/sketch-plugin-manifest-schema.json"."icon": "icon.png"."commands": [{"name": "my-command"."identifier": "sketch-placeimg.my-command-identifier"."script": "./my-command.js"}]."menu": {
    "title": "sketch-placeimg"."items": [
      "sketch-placeimg.my-command-identifier"]}}Copy the code

$SCHEMA looks like a JSON schema, and its corresponding JSON file address tells us which fields can be configured in it. The commands and menu fields are the most important.

There is only one command. The name of the command is my-command. The ID of the command is sketch-placeimg.my-command-identifier. The corresponding execution script is./ my-command-.js.

Menu marks the plugin’s navigation menu configuration, such as in the example where it specifies the plugin’s title in the plugin menu as Sketch – placeIMG and has a submenu, This corresponds to the command whose ID is sketch-placeimg.my-command-identifier. With this ID, the behavior of the menu is associated with executing the script.

appcast.xml

The manifest.json default example has two important fields that are not configured, version and Appcast. Version is clearly used to represent the current version of the plug-in. The appCast value is the URL of an XML file containing all versions of the plug-in and the corresponding download address. Sketch compares the VERSION corresponding to version with the XML corresponding to AppCast. If a new version is found, the plug-in is downloaded using the corresponding download address and updated online. An appcast.xml file might look something like this.

<? The XML version = "1.0" encoding = "utf-8" standalone = "yes"? > < RSS XMLNS: sparkle = "http://www.andymatuschak.org/xml-namespaces/sparkle" XMLNS: dc = "http://purl.org/dc/elements/1.1/" Version = "2.0" > < channel > < item > < enclosure Url = "https://github.com/lizheming/sketch-placeimg/releases/download/v0.1.1/sketch-placeimg.sketchplugin.zip" Sparkle: version = "while" / > < / item > < item > < enclosure Url = "https://github.com/lizheming/sketch-placeimg/releases/download/v0.1.0/sketch-placeimg.sketchplugin.zip" Sparkle: version = "0.1.0 from" / > < / item > < channel > < / RSS >Copy the code

If you publish the plug-in via the SKPM publish command, an.appcast.xml file is automatically generated in the root directory. Of course, according to the official documentation “Update a Plugin”, you can also build it manually.

resource

SKPM reads all commands from the manifest file specified in package.json as the compile entry file. Compile and package these documents into the


/Contents/Sketch directory. All assets files are copied to

/Contents/Resources. Finally, the plug-in generation is completed.

In other words, only webpack compilation is required to be a plugin command. The resource configuration is used if there are dependent non-plug-in resources, such as the JS file that the plug-in embedded in the HTML page depends on, that want to be compiled. The files configured in the Resource configuration are packaged in the webpack compiler and printed to the


/Contents/Resources directory.

Plug-in development

With some basic principles understood, we can proceed to the development of the plug-in. First we need the user to click on the plugins menu to open a panel that can be configured with basic information such as size and category.

In Sketch plug-in, we can use the native writing method to develop the panel, but it is troublesome to write the UI in this way, and it is higher for the front-end students to get started. So generally everyone will use a WebView to load the form of web development. WebView loading web pages by using the theory basically equivalent to the mobile end, client calls the WebView method loading web pages, through example webContents. ExecuteJavaScript () method for the plugin to the page of the communication, The web page uses the redefined window.postMessage to communicate with the plug-in.

sketch-module-web-view

To load a web page in a plugin, you need to install the Sketch-module-web-view plugin wrapped in Sketch.

npm install sketch-module-web-view --save-dev
Copy the code
// src/my-command.js
import BrowserWindow from 'sketch-module-web-view';
export default function() {
  const browserWindow = new BrowserWindow({
    width: 510.height: 270.resizable: false.movable: false.alwaysOnTop: true.maximizable: false.minimizable: false
  });
  browserWindow.loadURL(require('.. /resources/webview.html'))}Copy the code

When you’re done you’ll notice that nothing happens when you click on the Plugins menu because you still need to change the configuration. As you can see, we ended up importing an HTML file using require(). The official default template does not provide HTML import support, so we need to add a corresponding Webpack loader for the HTML file.

We need htMl-loader and @skpm/extract-loader here. The former is used to parse and handle resource associations that may exist in HTML code such as or . The latter is used to copy the HTML file to the


/Contents/Resources directory and return the corresponding file path URL in file:/// format for association in the plug-in.

npm install html-loader @skpm/extract-loader --save-dev
Copy the code

Create a webpack.skpm.config.js file in the root directory of the project. The first parameter that the exported method receives is the final Webpack configuration of the plugin. We can modify it directly on this basis.

// webpack.skpm.config.js
module.exports = function (config, entry) {
  config.module.rules.push({
    test: /\.html$/,
    use: [
      { loader: "@skpm/extract-loader" },
      {
        loader: "html-loader".options: {
          attributes: {
            list: [{tag: 'img'.attribute: 'src'.type: 'src' },
              { tag: 'link'.attribute: 'href'.type: 'src'}}}}]}); }Copy the code

The html-loader plugin has made some changes to the configuration format in the new version, so many of the old tutorials reported errors in the configuration. Of course, if you have more plug-in requirements, you can also follow this process to add configuration objects. Then we execute NPM Run Watch and click on the menu to see the page we expect.

Note: A set of template with Sketch -module-web-view module is provided officially. In order to explain the principle and process of the plug-in more clearly, I will explain it step by step with his family. In real development scenarios, you are advised to use the following commands to perform quick initialization.

skpm create <plugin-name> --template=skpm/with-webview
Copy the code

The React of integration

React Desktoop is a React component that does a good job of emulating Mac OSX UI style on the Web (although there are only a few forms to simulate).

The nice thing is that SKPM’s default WebPack configuration already has React support, so we don’t need to add additional WebPack configuration, just install the React dependencies and start developing.

npm install react react-dom react-desktop --save-dev
Copy the code

Add the webView.js entry file. Since this file needs to be compiled by Webpack but is not an execution file for the plug-in command, we need to add the entry file to the skpm.resources configuration of package.json as mentioned above.

// package.json { "skpm": { "resources": [ "resources/webview.js" ] } } // resources/webview.js import React from 'react'; import ReactDOM from 'react-dom'; function App() { return (<> <p>Hello World! </p> <hr /> via: <em>@lizheming</em> </>) } ReactDOM.render(<App />, document.getElementById('app'));Copy the code

Webview.html also needs a revamp to introduce JS entry files. Note here.. /resource_webview.js The reference file address, which is the final file address of the js entry file after compilation. This is mainly because the HTML file will eventually be generated in

.sketchplugin/Resources/_webpack_resources, and the JS entry file will replace the/delimiter with the _ delimiter. Generated under

.sketchplugin/Resources.

<! DOCTYPEhtml>
<html lang="zh-CN">
  <head>
    <meta charset="utf-8" />
    <title>PlaceIMG</title>
  </head>
  <body>
    <div id="app"></div>
    <script src=".. /resources_webview.js"></script>
  </body>
</html>
Copy the code

Note:

  1. HTML file generated to_webpack_resourcesconfiguration
  2. JS entry file generated toResourceDirectory configuration

Panel development

Now that the process is clear, we can focus on developing the panel. The panel development section is not much description, is nothing more than the writing of the front section of the page, the final plug-in panel will look like this.

-_ – | | well, actually I just want to tell you the process on hard the React…

After selecting and clicking Insert, call the postMessage() method to pass the final configuration to the plug-in.

//resources/webview.js
import React, {useReducer} from 'react';

function App() {
  const [{width, height, category, filter}, dispatch] = useReducer(
    (state, {type, ... payload}) = >({... state, ... payload}), {width: undefind, height: undefined.category: 'any'.filter: 'none'});const onInsert = _= > postMessage('insert', width, height, category, filter);
  return (
    <button onClick={onInsert}>insert</button>
  );
}
Copy the code

Note: The syntax of the Web native postMessage() method is postMessage(message, targetOrigin, [Transfer]). Both event names and event parameters should be serialized and passed in as message parameters.

The postMessage() method in the Sketch plugin is the injection method, which is a carbon copy of the native method, so the parameter format is different from the native one. See sketch-module-web-view code for an implementation of the injection method.

In the plugin, we listen for the Insert event, grab the configuration selected by the user and insert the generated image layer into the artboard.

//src/my-command.js
import sketch, { Image, Rectangle } from 'sketch/dom';
import BrowserWindow from 'sketch-module-web-view';

export default function() {
  const browserWindow = newBrowserWindow({... }); browserWindow.webContents.on('insert'.function(width, height, category, filter) {
    const url = 'https://placeimg.com/' + [width, height, category, filter].join('/');
    new Image({
      image:  NSURL.URLWithString(url),
      parent: getSelectedArtboard(),
      frame: new Rectangle(0.0, width, height),
    });
    return browserWindow.close();
  });
}
Copy the code

Plug-in release

Finally, the main functionality of our plug-in is developed. Now we are ready to publish the plug-in. We can publish directly using SKPM publish, which requires you to specify the Github repository address for the plug-in via SKPM publish — repo-URL or the repository field in package.json.

Apply for a new Token for SKPM on the Personal Access Token page and check the repO operation permission. After logging in using SKPM login

, SKPM gains access to the project.

Finally, publish

with SKPM. As mentioned earlier, the.appcast.xml file is created in the project directory after publication, and a Release record of the corresponding version is published, providing a download address for the plug-in’s ZIP package. After publish, if your plugin is not already listed in the plugin central repository, you will be asked if you want to submit a PR to add your plugin.

Of course, if your plugin is not convenient to publish on Github, you can also use the manual publishing described above. After executing SKPM build, you can package the generated

. Sketchplugin directory.

Plug-in to debug

The sample plug-in above is relatively simple, so it doesn’t use much debugging. The official tutorial Debug a Plugin describes several ways in which you can Debug. You can use the Console. App of the system to view the logs, or you can use the SKPM log -f plug-in log.

Most of the documentation is about plug-in debugging. Front-end code debugging in WebView is a little easier. Right-click the WebView form to review the elements and use Safari’s developer tools for debugging.

Note: The code nature of the plug-in itself is the client code, and the WebView is the front-end code, so the debugging and log output positions of the two are different, so we should pay attention to distinguish.

Afterword.

That’s the basics and simple process of developing Sketch. The rest is to look at the Sketch API documentation. However, the JavaScript API of Sketch is not perfect in practical use, and some functions may need to be differentiated by the native API for the time being. At this time you can Google, can find a lot of previous implementation, save their own workload.

This article mainly introduces a set of JavaScript API + WebView front-end development way, code I have been put on Github github.com/lizheming/s… , you can check and download by yourself. In addition to this approach, we can also use OC + WebView or even pure OC client to develop plug-ins. Using pure client development performance will be better than the form of JavaScript API, but for the front-end students do not understand OC development is relatively difficult to get started.

Besides Sketch, Figma is a great UI design software. It is based on Web development, naturally cross-platform, but also provides a more easy-to-use collaboration mode, to solve the problem of multi-user collaboration in UI development. Those of you who are interested can also go and check it out.

References:

  1. Sketch Plugin Development Summary