Plugin Development - Any up-to-date resources available?

I’m really lost on this one:

Months ago I started scripting my own little plugins for various repeating tasks I got. Following the API Documentation, I got the very basics up and running in no time. But oh boy, when I got to the “fancy” stuff, even Google and GitHub quickly ran out of answers or resources.

I dug deep into the structure of documents and layers and tried to reverse engineer my way through, but to the day I have found no reasonable method to deal with foreignObjects, foreignSwatches, foreignColors and their likes.

The very basics of retrieving the belonging swatches and names of colours in my document: unsolvable if not local.

So my question being: Are there any native workarounds tob expand the functionality of the incredibly limited js api?

Examples, hints, documentation, every snippet is welcome. The colour task is hunting me for weeks now and I’d love to put an end to it, and to others. Thanks in advance!

Hello and welcome to the forum :waving_hand:

First of all, please reconsider writing Sketch plugins in 2025. The whole plugin infrastructure is barely supported at this point, and Sketch folks made it clear that this won’t change anytime soon.

As someone who’s been building automation tools for Sketch practically full-time since 2016, the best advice I can give you is to spend your time and energy on something else, rather than fighting this never-ending uphill battle that is making a non-trivial Sketch plugin.

This advice comes from a place of huge love, not hate :yellow_heart:


Anyways, in order to deal with imported/foreign stuff you’d definitely need to drop to the native API layer.

Let’s say we have a Rectangle that references 2 color variables:

  • My Pink is a document-local variable used as a border color
  • Apple iOS UI / System Colors / systemGreen – Dark is a variable we’ve imported from the “Apple iOS UI” library, used as a fill color

Now, let’s start by taking this selected Rectangle and extracting raw native color objects from its fill and border:

const Document = require('sketch/dom').Document
const Swatch = require('sketch/dom').Swatch
const { toArray } = require('util');

const document = Document.getSelectedDocument()
const layer = document.selectedLayers.layers[0]

// These are native MSColor objects
const borderColor = layer.style.borders[0].sketchObject.color()
const fillColor = layer.style.fills[0].sketchObject.color()

The next step is to use find out if those native color objects have a reference to a swatch:

function swatchForMSColor(nativeColor) {
  const documentData = nativeColor.documentData()
  const swatchID = nativeColor.swatchID()
  if (!swatchID) { return null }
  
  return Swatch.from(documentData.swatchWithID(swatchID))
}

const borderColorSwatch = swatchForMSColor(borderColor)
const fillColorSwatch = swatchForMSColor(fillColor)

These swatches are some actual Sketch JS API objects so they print nicely and everything. The problem is that we don’t know which one is local and which one is imported from a library (and which library?). Let’s refactor swatchForMSColor() a bit and inject a few useful helper methods into the resulting swatch object:

Disclaimer: I’ve no idea how to write JavaScript when it comes to objects and classes. The code below works, but there’s probably a more idiomatic way to do it.

function swatchForMSColor(nativeColor) {
  const documentData = nativeColor.documentData()
  const swatchID = nativeColor.swatchID()
  if (!swatchID) { return null }
  
  let swatch = Swatch.from(documentData.swatchWithID(swatchID))
  
  // Returns an actual foreign swatch object if any
  swatch.foreignReference = function () {
    const nativeObject = this.sketchObject
    const allForeignSwatches = toArray(nativeObject.documentData().foreignSwatches()) || []
    return allForeignSwatches.find((s) => s.localSwatch().objectID().isEqualToString(nativeObject.objectID()))
  }
  // Returns `true` if this swatch comes from a library
  swatch.isImported = function() {
    return !!this.foreignReference();
  }
  // Returns a source library identifier for this swatch if one is available
  swatch.libraryID = function() {
    if (!this.isImported()) return null;
    return this.foreignReference().libraryID()
  }
  // Returns a source library name for this swatch if one is available
  swatch.libraryName = function() {
    if (!this.isImported()) return null;
    return this.foreignReference().sourceLibraryName()
  }

  return swatch
}

With that new code in place, let’s see which of those swatches is which:

console.log(`Is border color a variable? ${!!borderColorSwatch}`)
console.log(`    The variable name is "${borderColorSwatch.name}"`)
console.log(`Is border color a *library* variable? ${borderColorSwatch.isImported()}`)

// Is border color a variable? true
//     The variable name is "My Pink"
// Is border color a *library* variable? false

console.log(`Is fill color a variable? ${!!fillColorSwatch}`)
console.log(`Is fill color a *library* variable? ${fillColorSwatch.isImported()}`)
console.log(`Fill color variable "${fillColorSwatch.name}" comes from library "${fillColorSwatch.libraryName()}" (${fillColorSwatch.libraryID()})`) 

// Is fill color a variable? true
// Is fill color a *library* variable? true
// The color variable "System Colors/systemGreen - Dark" comes from library
//   "Apple iOS UI"  (DD59C2AC-DA86-465C-B5D7-FC1979E0DDB8)

Hope it helps!

2 Likes

this is sooo sad :frowning:

3 Likes

Whether it helps? Good lord, you basically did all the work for me, and yes, it will help and solve my task. Wow. :heart: Thank you!!!

I’ll never understand companies treating their development community this way, and usually, that behaviour makes them go out of business sooner or later. Let’s hope that’ll be different in this case.

So, a follow up question: is the native stuff documented somewhere? I’m not too lazy to look stuff up but really couldn’t find anything useful so far … :smiling_face_with_tear:

Well, the UI design world today is very different from what it was years ago when Sketch pioneered the field. Back then it was mostly power users who felt limited by contemporary design tools and wanted to do things differently: using vector graphics, thinking in terms of components, and automating their workflows – and it was power users again who drove Sketch sales and influenced its adoption by a broader design community.

Those power users still exist today, but they are no longer the driving force they used to be. So it makes sense for Sketch to shift focus and invest their limited resources towards things the broader design community would likely appreciate, which is everything besides plugins and integrations I guess?

Ah well, not really no. I guess you could get some insight into the Sketch internals by looking at Sketch JS API source code (which is built on top of native APIs), but for the most part you’d have to reverse-engineer Sketch binaries directly.

And of course I’d be happy to point you to the right direction if you have a specific request/question regarding the API :raising_hands:

1 Like

This thread saddens me. I remember reading some vague allusion to some bone being thrown to plug-in developers, but no news since.

Incidentally and anecdotally, I see a lot of conversation around the perceived lack of plugins and community resources for sketch compared to Figma. I think they need to do something

2 Likes